Introduction to Microservices
Microservices architecture structures an application as a collection of loosely coupled, independently deployable services. In 2025, microservices have evolved with cloud-native technologies, service mesh, and GraphQL APIs.
Monolith vs Microservices:
Monolithic Architecture:
+----------------------------------+
| Single Application |
| +----+ +----+ +----+ +----+ |
| |Auth| |User| |Order| |Pay | |
| | | | | | | | | |
| +----+ +----+ +----+ +----+ |
| Shared Database |
+----------------------------------+
Microservices Architecture:
+--------+ +--------+ +--------+
| Auth | | User | | Order |
|Service | |Service | |Service |
+--------+ +--------+ +--------+
| | |
+-----------+-----------+
|
+---------------+
| API Gateway |
+---------------+
Benefits:
- Independent deployment
- Technology diversity
- Scalability per service
- Fault isolation
- Team autonomy
Challenges:
- Network latency
- Distributed complexity
- Data consistency
- Service discovery
- Monitoring & debuggingAPI Gateway Patterns
API Gateway as a Facade
API Gateway Architecture:
Client
|
v
+---------------+
| API Gateway |
+---------------+
|
+-----------+-----------+
| | |
+-----+-----+ +--+--+ +--+---+
| Auth | |User | |Order |
| Service | |Svc | |Svc |
+-----------+ +-----+ +------+
Responsibilities:
- Request routing
- API composition
- Authentication & authorization
- Rate limiting
- Response aggregation
- Protocol translationtypescript
import { APIGateway } from '@aws-sdk/client-apigateway';
import { Lambda } from '@aws-sdk/client-lambda';
class APIGatewayService {
private gateway: APIGateway;
private lambda: Lambda;
private routes: Map<string, RouteConfig>;
constructor() {
this.routes = new Map();
this.setupRoutes();
}
private setupRoutes(): void {
// Authentication routes
this.routes.set('/auth/login', {
service: 'auth-service',
method: 'POST',
rateLimit: 10, // 10 requests per minute
authRequired: false
});
// User routes
this.routes.set('/users/{id}', {
service: 'user-service',
method: 'GET',
rateLimit: 100,
authRequired: true
});
// Order routes
this.routes.set('/orders', {
service: 'order-service',
method: 'POST',
rateLimit: 50,
authRequired: true
});
}
async handleRequest(request: Request): Promise<Response> {
// Extract route
const route = this.matchRoute(request.url, request.method);
if (!route) {
return new Response('Not Found', { status: 404 });
}
// Check authentication
if (route.authRequired) {
const authResult = await this.authenticate(request);
if (!authResult.valid) {
return new Response('Unauthorized', { status: 401 });
}
request.user = authResult.user;
}
// Apply rate limiting
const rateLimitResult = await this.checkRateLimit(
request,
route.rateLimit
);
if (!rateLimitResult.allowed) {
return new Response('Too Many Requests', {
status: 429,
headers: {
'Retry-After': rateLimitResult.retryAfter.toString()
}
});
}
// Route to backend service
return await this.proxyToService(route.service, request);
}
private async authenticate(
request: Request
): Promise<{ valid: boolean; user?: any }> {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return { valid: false };
}
try {
// Validate JWT token
const payload = await this.verifyToken(token);
return { valid: true, user: payload };
} catch (error) {
return { valid: false };
}
}
private async checkRateLimit(
request: Request,
limit: number
): Promise<{ allowed: boolean; retryAfter?: number }> {
const clientId = this.getClientId(request);
const key = `rate_limit:${clientId}`;
const current = await this.redis.incr(key);
if (current === 1) {
await this.redis.expire(key, 60); // 1 minute window
}
if (current > limit) {
const ttl = await this.redis.ttl(key);
return { allowed: false, retryAfter: ttl };
}
return { allowed: true };
}
private async proxyToService(
serviceName: string,
request: Request
): Promise<Response> {
const serviceUrl = this.getServiceUrl(serviceName);
try {
const response = await fetch(serviceUrl + request.url, {
method: request.method,
headers: request.headers,
body: request.body
});
return response;
} catch (error) {
// Circuit breaker pattern
await this.circuitBreaker.recordFailure(serviceName);
return new Response('Service Unavailable', { status: 503 });
}
}
}BFF (Backend for Frontend) Pattern
BFF Architecture:
Mobile Client Web Client
| |
v v
+-------+ +-------+
| Mobile | | Web |
| BFF | | BFF |
+-------+ +-------+
| |
+-------+---------+
|
+-------+-------+
| Microservices |
+---------------+
Benefits:
- Optimized data format per client
- Reduced network calls
- Client-specific logic
- Better mobile performancetypescript
class MobileBFF {
async getDashboard(userId: string): Promise<MobileDashboard> {
// Parallel calls to multiple services
const [user, orders, notifications] = await Promise.all([
this.userService.getProfile(userId),
this.orderService.getRecentOrders(userId, 5),
this.notificationService.getUnread(userId)
]);
// Mobile-optimized response
return {
user: {
name: user.name,
avatar: user.avatar // Thumbnail size
},
orders: orders.map(o => ({
id: o.id,
status: o.status,
total: o.total,
items: o.items.length // Just count for mobile
})),
notificationCount: notifications.length
};
}
}
class WebBFF {
async getDashboard(userId: string): Promise<WebDashboard> {
const [user, orders, notifications] = await Promise.all([
this.userService.getProfile(userId),
this.orderService.getRecentOrders(userId, 20),
this.notificationService.getAll(userId)
]);
// Web-optimized response with full details
return {
user: {
name: user.name,
email: user.email,
avatar: user.avatar, // Full resolution
preferences: user.preferences
},
orders: orders.map(o => ({
...o,
items: o.items.map(item => ({
...item,
product: {
name: item.product.name,
images: item.product.images, // All images
description: item.product.description
}
}))
})),
notifications: notifications.map(n => ({
...n,
fullMessage: n.message
}))
};
}
}Service Mesh
What is Service Mesh?
Service Mesh Architecture:
Service Mesh (Istio/Linkerd)
|
+---------------+---------------+
| | |
Service A Service B Service C
| | |
Sidecar Sidecar Sidecar
(Envoy) (Envoy) (Envoy)
| | |
+---------------+---------------+
|
Network/Infrastructure
Features:
- Service discovery
- Load balancing
- Traffic management
- Security (mTLS)
- Observability (metrics, traces, logs)
- Resilience (retries, timeouts, circuit breaking)Implementing Service Mesh with Istio
yaml
# VirtualService for traffic management
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: user-service
subset: v2
weight: 100
- route:
- destination:
host: user-service
subset: v1
weight: 90
- destination:
host: user-service
subset: v2
weight: 10
---
# DestinationRule for subsets
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
# ServiceEntry for external services
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: external-api
spec:
hosts:
- api.external.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNSTraffic Management Patterns
typescript
class IstioTrafficManager {
async deployCanary(
serviceName: string,
newVersion: string,
initialTraffic: number
): Promise<void> {
// Create new deployment
await this.k8s.apply({
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: { name: `${serviceName}-${newVersion}` },
spec: {
replicas: 1,
selector: {
matchLabels: { version: newVersion }
},
template: {
metadata: {
labels: {
app: serviceName,
version: newVersion
}
},
spec: {
containers: [{
name: serviceName,
image: `registry/${serviceName}:${newVersion}`
}]
}
}
}
});
// Update VirtualService for canary
await this.updateVirtualService(serviceName, {
route: [
{
destination: {
host: serviceName,
subset: 'stable'
},
weight: 100 - initialTraffic
},
{
destination: {
host: serviceName,
subset: newVersion
},
weight: initialTraffic
}
]
});
// Create DestinationRule
await this.updateDestinationRule(serviceName, {
subsets: [
{ name: 'stable', labels: { version: 'stable' } },
{ name: newVersion, labels: { version: newVersion } }
]
});
}
async gradualRollout(
serviceName: string,
version: string,
steps: number[]
): Promise<void> {
for (const traffic of steps) {
console.log(`Rolling out ${traffic}% traffic to ${version}`);
await this.updateVirtualService(serviceName, {
route: [
{
destination: {
host: serviceName,
subset: 'stable'
},
weight: 100 - traffic
},
{
destination: {
host: serviceName,
subset: version
},
weight: traffic
}
]
});
// Wait and monitor
await this.waitForDuration(5 * 60); // 5 minutes
const metrics = await this.getMetrics(serviceName, version);
if (metrics.errorRate > 0.01) { // 1% error threshold
console.error('High error rate, rolling back');
await this.rollback(serviceName);
throw new Error('Canary failed');
}
}
console.log(`Full rollout to ${version} successful`);
}
async implementFaultInjection(
serviceName: string,
fault: FaultConfig
): Promise<void> {
await this.updateVirtualService(serviceName, {
fault: {
delay: fault.delay ? {
percentage:
value: fault.delay.percentage,
fixedDelay: fault.delay.duration
} : undefined,
abort: fault.abort ? {
percentage:
value: fault.abort.percentage,
httpStatus: fault.abort.statusCode
} : undefined
}
});
}
async setupCircuitBreaker(
serviceName: string,
config: CircuitBreakerConfig
): Promise<void> {
await this.updateDestinationRule(serviceName, {
trafficPolicy: {
outlierDetection: {
consecutive5xxErrors: config.consecutiveErrors,
interval: config.interval,
baseEjectionTime: config.ejectionTime,
maxEjectionPercent: config.maxEjectionPercent
},
connectionPool: {
tcp: {
maxConnections: config.maxConnections
},
http: {
http1MaxPendingRequests: config.maxPendingRequests,
http2MaxRequests: config.maxRequests
}
}
}
});
}
}Inter-Service Communication
Synchronous Communication
typescript
// REST with resilience patterns
class ResilientServiceClient {
private circuitBreaker: CircuitBreaker;
private retryPolicy: RetryPolicy;
async callService(
serviceName: string,
endpoint: string,
data: any
): Promise<any> {
return this.circuitBreaker.execute(async () => {
return this.retryPolicy.execute(async () => {
const response = await fetch(
`http://${serviceName}${endpoint}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Request-ID': this.generateRequestId()
},
body: JSON.stringify(data),
timeout: 5000 // 5 second timeout
}
);
if (!response.ok) {
throw new ServiceError(
`Service ${serviceName} returned ${response.status}`,
response.status
);
}
return await response.json();
});
});
}
}
// gRPC for high-performance inter-service communication
import { grpc } from '@grpc/grpc-js';
class OrderServiceClient {
private client: any;
constructor() {
this.client = new OrderServiceClient(
'order-service:50051',
grpc.credentials.createInsecure()
);
}
async createOrder(order: Order): Promise<OrderResponse> {
return new Promise((resolve, reject) => {
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5); // 5s timeout
this.client.createOrder(
{
userId: order.userId,
items: order.items.map(item => ({
productId: item.productId,
quantity: item.quantity
})),
timestamp: order.timestamp
},
{ deadline },
(error: any, response: any) => {
if (error) {
reject(error);
} else {
resolve(response);
}
}
);
});
}
}Asynchronous Communication
typescript
// Event-driven architecture with Kafka
import { Kafka } from 'kafkajs';
class EventPublisher {
private producer: any;
private kafka: Kafka;
constructor() {
this.kafka = new Kafka({
clientId: 'order-service',
brokers: ['kafka1:9092', 'kafka2:9092', 'kafka3:9092']
});
this.producer = this.kafka.producer();
}
async publishOrderCreated(order: Order): Promise<void> {
const event: DomainEvent = {
eventId: uuidv4(),
eventType: 'OrderCreated',
timestamp: Date.now(),
version: 1,
data: {
orderId: order.id,
userId: order.userId,
total: order.total,
items: order.items
}
};
await this.producer.send({
topic: 'order-events',
messages: [{
key: order.id,
value: JSON.stringify(event),
headers: {
'event-type': 'OrderCreated',
'correlation-id': event.eventId
}
}]
});
}
async publishOrderCancelled(orderId: string, reason: string): Promise<void> {
const event: DomainEvent = {
eventId: uuidv4(),
eventType: 'OrderCancelled',
timestamp: Date.now(),
version: 1,
data: {
orderId,
reason
}
};
await this.producer.send({
topic: 'order-events',
messages: [{
key: orderId,
value: JSON.stringify(event)
}]
});
}
}
// Event consumer with Exactly-Once semantics
class EventConsumer {
private consumer: any;
async start(): Promise<void> {
await this.consumer.subscribe({
topic: 'order-events',
fromBeginning: false
});
await this.consumer.run({
eachMessage: async ({ topic, partition, message }) => {
const event = JSON.parse(message.value.toString());
try {
await this.processEvent(event);
// Commit offset after successful processing
await this.consumer.commitOffsets({
topic,
partition,
offset: message.offset + 1
});
} catch (error) {
console.error('Failed to process event:', error);
// Retry with backoff
throw error;
}
}
});
}
private async processEvent(event: DomainEvent): Promise<void> {
switch (event.eventType) {
case 'OrderCreated':
await this.handleOrderCreated(event.data);
break;
case 'OrderCancelled':
await this.handleOrderCancelled(event.data);
break;
default:
console.warn(`Unknown event type: ${event.eventType}`);
}
}
private async handleOrderCreated(data: any): Promise<void> {
// Update read model
await this.readModel.upsertOrder({
orderId: data.orderId,
userId: data.userId,
total: data.total,
status: 'CREATED'
});
// Send notification
await this.notificationService.send({
userId: data.userId,
type: 'ORDER_CONFIRMATION',
data: { orderId: data.orderId }
});
// Update analytics
await this.analytics.track({
event: 'order_created',
properties: {
orderId: data.orderId,
total: data.total,
itemCount: data.items.length
}
});
}
}Data Management Patterns
Database per Service Pattern
Database per Service:
+------------+ +------------+ +------------+
| Order | | User | | Inventory |
| Service | | Service | | Service |
+------------+ +------------+ +------------+
| | |
v v v
+------------+ +------------+ +------------+
| Order DB | | User DB | |InventoryDB|
+------------+ +------------+ +------------+
Benefits:
- Loose coupling
- Independent scaling
- Technology diversity
- Team autonomy
Challenges:
- Distributed transactions
- Data consistency
- Cross-service queriesSaga Pattern for Distributed Transactions
typescript
class OrderSagaOrchestrator {
async execute(orderId: string): Promise<void> {
const saga = new Saga();
// Step 1: Create order
saga.addStep(
async () => this.orderService.create(orderId),
async () => this.orderService.cancel(orderId)
);
// Step 2: Reserve inventory
saga.addStep(
async () => this.inventoryService.reserve(orderId),
async () => this.inventoryService.release(orderId)
);
// Step 3: Process payment
saga.addStep(
async () => this.paymentService.charge(orderId),
async () => this.paymentService.refund(orderId)
);
// Step 4: Schedule shipping
saga.addStep(
async () => this.shippingService.schedule(orderId),
async () => this.shippingService.cancel(orderId)
);
await saga.execute();
}
}
// Choreography-based Saga
class OrderService {
async createOrder(order: Order): Promise<void> {
const orderId = uuidv4();
// Save order
await this.db.orders.insert({
id: orderId,
...order,
status: 'PENDING'
});
// Publish event
await this.eventBus.publish({
eventType: 'OrderCreated',
data: { orderId, ...order }
});
}
async onOrderPaid(event: OrderPaidEvent): Promise<void> {
// Update order status
await this.db.orders.update(event.orderId, {
status: 'PAID'
});
// Trigger next step
await this.eventBus.publish({
eventType: 'OrderPaymentConfirmed',
data: { orderId: event.orderId }
});
}
async onInventoryReservationFailed(event: Event): Promise<void> {
// Compensate: cancel order
await this.db.orders.update(event.orderId, {
status: 'CANCELLED',
reason: 'Inventory unavailable'
});
// Notify user
await this.notificationService.send({
userId: event.userId,
type: 'ORDER_CANCELLED',
data: { orderId: event.orderId }
});
}
}Observability in Microservices
Distributed Tracing
typescript
import { trace } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
class TracingSetup {
static initialize(): void {
const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'order-service',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0'
})
});
const exporter = new JaegerExporter({
endpoint: 'http://jaeger:14268/api/traces'
});
provider.addSpanProcessor(
new BatchSpanProcessor(exporter)
);
provider.register();
// Instrumentations
new ExpressInstrumentation().setTracerProvider(provider);
new HttpInstrumentation().setTracerProvider(provider);
}
}
// Usage in service
class OrderService {
async createOrder(order: Order): Promise<string> {
const tracer = trace.getTracer('order-service');
return tracer.startActiveSpan('createOrder', async (span) => {
try {
span.setAttribute('user.id', order.userId);
span.setAttribute('order.total', order.total);
const orderId = await this.saveOrder(order);
span.setStatus({ code: SpanStatusCode.OK });
return orderId;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
throw error;
} finally {
span.end();
}
});
}
}Service Health Monitoring
typescript
class HealthCheckService {
private checks: Map<string, HealthCheck>;
constructor() {
this.checks = new Map();
this.setupDefaultChecks();
}
private setupDefaultChecks(): void {
this.register('database', async () => {
try {
await this.db.query('SELECT 1');
return { status: 'healthy' };
} catch (error) {
return {
status: 'unhealthy',
error: error.message
};
}
});
this.register('redis', async () => {
try {
await this.redis.ping();
return { status: 'healthy' };
} catch (error) {
return {
status: 'unhealthy',
error: error.message
};
}
});
this.register('kafka', async () => {
try {
const admin = this.kafka.admin();
await admin.connect();
await admin.disconnect();
return { status: 'healthy' };
} catch (error) {
return {
status: 'unhealthy',
error: error.message
};
}
});
}
register(name: string, check: HealthCheckFunction): void {
this.checks.set(name, check);
}
async getHealth(): Promise<HealthReport> {
const results: Map<string, HealthCheckResult> = new Map();
for (const [name, check] of this.checks) {
try {
const result = await check();
results.set(name, result);
} catch (error) {
results.set(name, {
status: 'unhealthy',
error: error.message
});
}
}
const overall = Array.from(results.values()).every(
r => r.status === 'healthy'
);
return {
status: overall ? 'healthy' : 'degraded',
checks: Object.fromEntries(results),
timestamp: new Date().toISOString()
};
}
}2025 Microservices Best Practices
1. Use Kubernetes for Orchestration:
- Declarative configuration
- Self-healing
- Auto-scaling
- Service discovery built-in
2. Implement Service Mesh:
- Zero-trust security
- Traffic management
- Observability out of the box
- Policy enforcement
3. Embrace GraphQL for APIs:
- Single gateway
- Client-specific queries
- Reduced over-fetching
- Strong typing
4. Use Event-Driven Architecture:
- Loose coupling
- Better scalability
- Natural audit trail
- Event sourcing
5. Implement Chaos Engineering:
- Test resilience
- Proactive failure testing
- Improve reliability
- Build confidence
6. Adopt GitOps:
- Declarative infrastructure
- Version control
- Automated deployments
- Rollback capability
7. Use Feature Flags:
- Progressive delivery
- A/B testing
- Quick rollbacks
- Experiment safely
8. Invest in Observability:
- Distributed tracing
- Real-time metrics
- Structured logging
- Alert on SLOsBuild scalable, resilient microservices with modern patterns and practices!