Microservices Design Patterns
Complete pattern catalog for building production-grade microservices
Microservices patterns fall into five categories: Decomposition (how to split), Integration (how services talk), Data Management (how data is handled), Observability (how to monitor), and Cross-cutting (shared concerns).
1. Decomposition Patterns
How to break down your monolith into microservices the right way.
Split based on business functions.
Examples:
- E-Commerce: Order, Payment, Inventory, Shipping
- Banking: Account, Transaction, Loan, Customer
Identify Bounded Contexts and define Ubiquitous Language.
Examples:
- Insurance: Policy, Claims, Underwriting, Billing
Split by actions: Create, Update, Delete, Query.
Examples:
- OrderWrite Service, OrderRead Service
2. Integration Patterns
How services communicate and discover each other.
Single entry point for all clients, handling routing, authentication, rate limiting, and response aggregation.
Request Flow:
Client -> API Gateway ->
Auth Service (validate token)
Order Service (get orders)
Payment Service (get payments)
Response Aggregation -> Client
Services dynamically find each other without hardcoded addresses. Essential for cloud environments.
Service Registration (on startup): Payment Service -> Service Registry: "I am at 10.0.0.5:8080" Service Discovery (when needed): Order Service -> Service Registry: "Where is Payment Service?" Service Registry -> Order Service: "Payment Service at 10.0.0.5:8080"
Prevents cascading failures when a downstream service is failing.
Three states of Circuit Breaker:
CLOSED -> Calls succeed normally
(after 5 failures)
OPEN -> Calls fail immediately (no timeout wait)
(after timeout period)
HALF-OPEN -> Allow test call to check recovery
Handle transient failures with smart retry logic.
Exponential Backoff Strategy: Attempt 1: immediate Attempt 2: wait 100ms Attempt 3: wait 200ms Attempt 4: wait 400ms Attempt 5: wait 800ms -> then fail
3. Data Management Patterns
Each microservice has its own private database. No sharing!
Order Service -> order_db (PostgreSQL) Payment Service -> payment_db (PostgreSQL) User Service -> user_db (MongoDB) Analytics Service -> analytics_db (Cassandra)
Decentralized coordination where services publish events that trigger actions in other services.
Order Created --event--> Payment Service
(Payment Success)
Inventory Service --event--> Shipping Service
(Stock Reserved) (Create Shipment)
Centralized coordinator that tells each service what to do and when.
Orchestrator -> Order Service (create order)
-> Payment Service (process payment)
-> Inventory Service (reserve stock)
-> Shipping Service (create shipment)
[If any fails, send compensating commands]
Separate models for writes (commands) and reads (queries). Optimize each independently.
Command Model (Write) Query Model (Read)
OrderWrite Service --event--> OrderRead Service
(optimized for updates) (denormalized for fast queries)
4. Observability Patterns
Track a request across multiple services with a single trace ID.
Trace ID: abc-123
Gateway (5ms) -> Order (50ms) -> Payment (200ms)
Tools: Jaeger, Zipkin, OpenTelemetry
GET /health
{
"status": "UP",
"components": {
"database": {"status": "UP"},
"redis": {"status": "UP"}
}
}
Centralize logs from all services for searching and analysis.
Tools: ELK Stack (Elasticsearch, Logstash, Kibana), Loki, Splunk
Collect and visualize system metrics (latency, throughput, error rates).
Tools: Prometheus, Grafana, Datadog
5. Cross-cutting Patterns
Deploy auxiliary components alongside your main service container.
Main Container + Logging Sidecar + Monitoring Sidecar + Config Sidecar
Proxy that handles external communication for a service (retry, circuit breaker, monitoring).
Example: Kubernetes service mesh (Istio, Linkerd)
Configuration stored outside the service code, loaded at runtime.
Config Server -> Service reads config on startup/refresh
Tools: Spring Cloud Config, Consul, etcd
Pattern Selection Decision Tree
Q1: How to split the monolith?
-> Simple business logic? Use Decompose by Business Capability
-> Complex domain? Use Decompose by Subdomain (DDD)
Q2: How do services communicate?
-> Need immediate response? Use Sync (API Gateway)
-> Can wait? Use Async (Event-Driven)
Q3: How to handle service failures?
-> Use Retry + Circuit Breaker + Timeout + Fallback
Q4: How to manage distributed transactions?
-> Simple workflow? Use Saga Choreography
-> Complex workflow? Use Saga Orchestration
Q5: Read vs Write performance issues?
-> Use CQRS + Event Sourcing
Q6: Need to share cross-cutting concerns?
-> Use Sidecar pattern
Q7: How to find services dynamically?
-> Use Service Discovery
Pattern Summary Table
| Category | Pattern | When to Use |
|---|---|---|
| Decomposition | Business Capability | Simple business logic, clear boundaries |
| Decomposition | Subdomain (DDD) | Complex domain, domain experts available |
| Integration | API Gateway | Multiple clients, need centralized auth/routing |
| Integration | Circuit Breaker | Prevent cascading failures |
| Data Management | Database per Service | Always - non-negotiable for true microservices |
| Data Management | Saga | Distributed transactions across services |
| Data Management | CQRS | Different read/write performance needs |
| Observability | Distributed Tracing | Debugging requests across service boundaries |
| Cross-cutting | Sidecar | Shared concerns across services |
"Database per service is non-negotiable. If services share a database, you don't have microservices - you have a distributed monolith with extra network calls. The Circuit Breaker pattern is your first line of defense against cascading failures."
Key Takeaways
- Decomposition first - get your boundaries right before building
- API Gateway is the entry point for all external traffic
- Circuit Breakers are mandatory for production systems
- Database per Service - no exceptions
- Saga for distributed transactions, CQRS for read/write separation
- Observability (tracing, logs, metrics) is not optional
- Sidecar pattern for infrastructure concerns
"Patterns are solutions to recurring problems, not mandatory prescriptions. The best architecture selectively applies patterns based on actual needs, not theoretical elegance. Start simple, add patterns only when you feel the pain they solve."