Microservices Patterns and Anti-Patterns
Microservices promise independent deployability, team autonomy, and technology flexibility. They also deliver distributed systems complexity, network unreliability, and operational overhead. Knowing which patterns to apply -- and which anti-patterns to avoid -- separates successful implementations from distributed monoliths.
Decomposition Strategies
How you split a monolith matters more than whether you split it.
| Strategy | Approach | When to Use |
|---|---|---|
| By business capability | Align services to business domains (orders, payments, inventory) | Stable domain boundaries |
| By subdomain (DDD) | Use Domain-Driven Design bounded contexts | Complex domains with clear separations |
| Strangler fig | Gradually extract services from a monolith | Incremental migration |
| By team | One service per team (inverse Conway's Law) | Organizational alignment |
Inter-Service Communication
| Pattern | Protocol | Use Case | Trade-off |
|---|---|---|---|
| Synchronous REST | HTTP/JSON | Simple CRUD operations | Tight coupling, cascade failures |
| Synchronous gRPC | HTTP/2 + Protobuf | High-performance internal APIs | Schema management, less human-readable |
| Async messaging | AMQP, Kafka | Event-driven workflows | Eventual consistency, harder debugging |
| Async request-reply | Correlation IDs over queues | Long-running operations | Complexity in tracking requests |
The Saga Pattern for Distributed Transactions
Traditional ACID transactions do not span services. The saga pattern coordinates multi-service operations:
Choreography-based saga:
- Each service publishes events and listens for events from others
- No central coordinator
- Simple for 2-3 services, complex for more
Orchestration-based saga:
- A central orchestrator directs the workflow
- Each service exposes a compensating action for rollback
- Easier to understand, but the orchestrator is a single point of logic
| Aspect | Choreography | Orchestration |
|---|---|---|
| Coupling | Loose | Medium (to orchestrator) |
| Visibility | Hard to trace full flow | Clear workflow definition |
| Complexity at scale | Grows exponentially | Grows linearly |
| Best for | Simple workflows | Complex multi-step processes |
Service Mesh
A service mesh handles cross-cutting concerns at the infrastructure layer:
- Traffic management -- routing, load balancing, canary deployments
- Security -- mutual TLS, authorization policies
- Observability -- distributed tracing, metrics, access logs
- Resilience -- retries, circuit breakers, timeouts
Popular options: Istio (feature-rich, complex), Linkerd (lightweight, simpler), Consul Connect (HashiCorp ecosystem).
Critical Anti-Patterns
| Anti-Pattern | Symptom | Fix |
|---|---|---|
| Distributed monolith | Services must be deployed together | Enforce API contracts, reduce shared databases |
| Shared database | Multiple services write to the same tables | Database per service, events for sync |
| Chatty services | Hundreds of inter-service calls per request | Aggregate APIs, BFF pattern |
| Nano-services | Services too small to justify overhead | Merge related services |
| God service | One service handles too much logic | Decompose by bounded context |
| Sync chain | A calls B calls C calls D synchronously | Async messaging, event-driven |
When Monolith Is Better
Microservices are not always the answer:
- Small team (< 10 engineers) -- operational overhead outweighs benefits
- Early-stage product -- domain boundaries are not yet clear
- Simple domain -- CRUD applications do not need distributed complexity
- Low scale -- if a single server handles the load, do not distribute it
- Tight latency requirements -- every network hop adds latency
A well-structured modular monolith can deliver most of the organizational benefits of microservices without the distributed systems tax.