Microservices Design Patterns Overview
IntermediateCore patterns include API Gateway, Service Registry, Circuit Breaker, Saga, CQRS, Event Sourcing, Sidecar, and Strangler Fig — each solves a specific distributed-systems challenge.
Overview
Microservices introduce distributed systems challenges that monoliths do not have: service discovery, partial failure, cross-service transactions, and data consistency. A library of well-known patterns addresses these challenges. Key structural patterns: API Gateway (single entry point), Service Registry (dynamic discovery), Sidecar (per-pod infrastructure). Key resilience patterns: Circuit Breaker (fail fast), Bulkhead (isolate failure), Retry with backoff. Key data patterns: Saga (distributed transactions), CQRS (read/write model split), Event Sourcing (log as truth). Each pattern solves a specific problem — over-applying them adds unnecessary complexity.
Communication & Routing Patterns
API Gateway centralises cross-cutting concerns. Service Registry enables dynamic discovery. These two patterns almost always appear together in production microservices.
// Pattern 1: API Gateway
// Single entry point for all client requests
// Responsibilities: auth, rate limiting, routing, SSL termination, CORS
// Spring Cloud Gateway config
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service # lb:// = load-balanced via Eureka
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args: { name: orderCB, fallbackUri: forward:/fallback }
- name: RequestRateLimiter
args: { redis-rate-limiter.replenishRate: 100, redis-rate-limiter.burstCapacity: 200 }
// Pattern 2: Service Registry (Eureka)
// Services register on startup; gateway/clients look up instances
@EnableEurekaServer
@SpringBootApplication
public class DiscoveryServerApp { ... }
@EnableDiscoveryClient // on each microservice
@SpringBootApplication
public class OrderServiceApp { ... }Resilience Patterns
Circuit Breaker stops calling a failing service and returns a fallback. Bulkhead isolates thread pools so one slow service cannot starve all others. Retry with exponential backoff handles transient failures.
// Pattern 3: Circuit Breaker (Resilience4j)
@Service
public class ProductService {
@CircuitBreaker(name = "product-service", fallbackMethod = "fallbackProduct")
@Retry(name = "product-service")
@Bulkhead(name = "product-service", type = Bulkhead.Type.THREADPOOL)
public ProductDTO getProduct(Long id) {
return productClient.getById(id);
}
public ProductDTO fallbackProduct(Long id, Exception ex) {
return ProductDTO.unavailable(id); // degraded response
}
}
# Resilience4j config
resilience4j:
circuitbreaker:
instances:
product-service:
slidingWindowSize: 10
failureRateThreshold: 50 # open after 50% failures
waitDurationInOpenState: 10s # stay open for 10s then half-open
retry:
instances:
product-service:
maxAttempts: 3
waitDuration: 500ms
enableExponentialBackoff: true
bulkhead:
instances:
product-service:
maxConcurrentCalls: 10 # max parallel calls to product-serviceData Patterns: Saga & CQRS
Saga manages distributed transactions via a sequence of local transactions coordinated by events (choreography) or a central orchestrator. CQRS separates the write model (commands) from the read model (queries) for independent scaling.
// Pattern 4: Saga (Choreography-based)
// Each service publishes an event; the next service reacts
// Order Service — Step 1
public void placeOrder(PlaceOrderCommand cmd) {
Order order = new Order(cmd); order.setStatus(PENDING);
orderRepo.save(order);
eventBus.publish(new OrderPlaced(order.getId(), cmd.getItems()));
}
// Inventory Service — Step 2 (listens to OrderPlaced)
@EventHandler
public void on(OrderPlaced event) {
if (inventoryService.reserve(event.getItems())) {
eventBus.publish(new InventoryReserved(event.getOrderId()));
} else {
eventBus.publish(new InventoryReservationFailed(event.getOrderId()));
}
}
// Order Service — compensating transaction on failure
@EventHandler
public void on(InventoryReservationFailed event) {
orderRepo.findById(event.getOrderId())
.ifPresent(o -> { o.cancel(); orderRepo.save(o); });
eventBus.publish(new OrderCancelled(event.getOrderId()));
}
// Pattern 5: CQRS — separate read/write models
// Write model: OrderCommandService → command DB (normalised)
// Read model: OrderQueryService → read DB (denormalised, projected)Key Points to Remember
- 1API Gateway: single entry point for routing, auth, rate limiting, SSL termination.
- 2Service Registry (Eureka/Consul): services register on start; clients look up instances dynamically.
- 3Circuit Breaker: fail fast when a downstream is unhealthy; return fallback response.
- 4Saga: distributed transaction via compensating local transactions (choreography or orchestration).
- 5CQRS: separate write (command) and read (query) models for independent optimisation.
- 6Outbox Pattern: atomic message publishing by writing events to a DB table in the same transaction.
Interview Questions
Sign in to ask AriaWhat problem does the API Gateway pattern solve?
What is the difference between Saga choreography and Saga orchestration?
How does the Circuit Breaker pattern prevent cascading failures?
What is CQRS and when would you use it?
What is the Bulkhead pattern and how does it differ from Circuit Breaker?
Ask Aria about Microservices Design Patterns Overview
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.