Microservices Design Patterns
A comprehensive guide to essential design patterns for building robust microservices architectures.
Communication Patterns
1. API Gateway Pattern
Central entry point for all client requests.
┌────────────┐ ┌─────────────┐ ┌──────────────┐
│ Client │────▶│ API Gateway │────▶│ Services │
└────────────┘ └─────────────┘ └──────────────┘
Responsibilities:
- Request routing
- Rate limiting
- Authentication/Authorization
- Load balancing
- Response aggregation
Implementation (ASP.NET Core + Ocelot):
// ocelot.json
{
"Routes": [
{
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [{ "Host": "orders-service", "Port": 443 }],
"UpstreamPathTemplate": "/orders/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT" ]
}
]
}
2. Service Discovery
Dynamic service location without hardcoded addresses.
Client-Side Discovery:
// Using Consul
var services = await _consul.Health.Service("order-service", passing: true);
var selectedService = LoadBalance(services);
Server-Side Discovery:
- Load balancer queries service registry
- Kubernetes built-in DNS
3. Circuit Breaker
Prevent cascade failures when services are unavailable.
// Using Polly
var circuitBreaker = Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (ex, duration) => _logger.LogWarning("Circuit open"),
onReset: () => _logger.LogInformation("Circuit closed")
);
States:
- Closed: Normal operation
- Open: Requests fail immediately
- Half-Open: Testing if service recovered
Data Management Patterns
4. Database per Service
Each service owns its data store.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Order Svc │ │ User Svc │ │ Product Svc │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Orders DB │ │ Users DB │ │ Products DB │
│ (SQL) │ │ (Postgres) │ │ (MongoDB) │
└─────────────┘ └─────────────┘ └─────────────┘
Benefits:
- Loose coupling
- Independent scaling
- Technology freedom
- Data isolation
5. Saga Pattern
Distributed transactions across services.
Choreography (Event-driven):
public class OrderCreatedHandler : IHandleMessages<OrderCreated>
{
public async Task Handle(OrderCreated message)
{
var result = await _inventory.ReserveStock(message.Items);
if (result.Success)
await _bus.Publish(new StockReserved { OrderId = message.OrderId });
else
await _bus.Publish(new OrderCancelled { OrderId = message.OrderId });
}
}
Orchestration (Central coordinator):
public class CreateOrderSaga : Saga<CreateOrderSagaData>
{
public async Task Start(CreateOrder command)
{
await RequestTimeout<OrderTimeout>(TimeSpan.FromMinutes(5));
await _inventory.ReserveStock(command);
}
public async Task Handle(StockReserved message)
{
await _payment.ProcessPayment(Data.OrderId);
}
public async Task Handle(PaymentProcessed message)
{
await _shipping.CreateShipment(Data.OrderId);
MarkAsComplete();
}
}
6. CQRS (Command Query Responsibility Segregation)
Separate read and write models.
Commands Queries
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Write Model │───events──▶│ Read Model │
│ (Normalized) │ │ (Denormalized) │
└────────┬───────┘ └───────┬────────┘
│ │
┌────────▼───────┐ ┌───────▼────────┐
│ Write DB │ │ Read DB │
│ (PostgreSQL) │ │ (ElasticSearch)│
└────────────────┘ └────────────────┘
7. Event Sourcing
Store state as sequence of events.
public class Order : AggregateRoot
{
public void CreateOrder(CreateOrderCommand cmd)
{
Apply(new OrderCreated { OrderId = cmd.OrderId });
}
public void AddItem(AddItemCommand cmd)
{
Apply(new ItemAdded { ProductId = cmd.ProductId, Quantity = cmd.Quantity });
}
private void On(OrderCreated e) => Id = e.OrderId;
private void On(ItemAdded e) => Items.Add(new OrderItem(e.ProductId, e.Quantity));
}
Resilience Patterns
8. Retry Pattern
Automatic retry with backoff.
var retryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt =>
TimeSpan.FromSeconds(Math.Pow(2, attempt))
);
9. Bulkhead Pattern
Isolate resources to prevent cascade failures.
var bulkhead = Policy.BulkheadAsync(
maxParallelization: 10,
maxQueuingActions: 20
);
// Each service has isolated thread pool
var orderBulkhead = CreateBulkhead("orders", 10);
var paymentBulkhead = CreateBulkhead("payments", 5);
10. Sidecar Pattern
Deploy helper components alongside services.
# Kubernetes Pod with Sidecar
spec:
containers:
- name: main-app
image: my-service:latest
- name: logging-sidecar
image: fluentd:latest
- name: proxy-sidecar
image: envoy:latest
Deployment Patterns
11. Blue-Green Deployment
Zero-downtime releases.
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│ Blue │ │ Green │
│ (v1) │ │ (v2) │ ← New version
│ ACTIVE│ │ IDLE │
└───────┘ └───────┘
12. Canary Deployment
Gradual traffic shift to new version.
// Traffic routing example
public IEndpoint RouteRequest(string userId)
{
var hash = ComputeHash(userId);
if (hash % 100 < 5) // 5% to canary
return _canaryEndpoint;
return _stableEndpoint;
}
Observability Patterns
13. Distributed Tracing
Track requests across services.
// Using OpenTelemetry
using var activity = ActivitySource.StartActivity("ProcessOrder");
activity?.SetTag("order.id", orderId);
await _httpClient.PostAsync(url, content); // Automatically propagates trace context
14. Log Aggregation
Centralized logging from all services.
{
"timestamp": "2024-01-15T10:30:00Z",
"service": "order-service",
"traceId": "abc123",
"spanId": "def456",
"level": "INFO",
"message": "Order created",
"orderId": "ORD-001"
}
15. Health Check Pattern
Monitor service health.
public class DatabaseHealthCheck : IHealthCheck
{
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken)
{
try
{
await _db.ExecuteAsync("SELECT 1");
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy("Database unavailable", ex);
}
}
}
Pattern Selection Guide
| Scenario | Recommended Pattern |
|---|---|
| Cross-service transactions | Saga |
| High read/write ratio | CQRS |
| Audit requirements | Event Sourcing |
| Service unavailability | Circuit Breaker |
| Network instability | Retry + Timeout |
| Resource isolation | Bulkhead |
| Zero-downtime deploy | Blue-Green |
| Risk mitigation | Canary |
Sources
Arhitectura/microservices design patterns.png