1---2name: microservices3description: Microservices architecture patterns including service decomposition, communication (sync/async, gRPC), API gateway, Saga, CQRS, event sourcing, transactional outbox, data management, and fault isolation (Circuit Breaker, Bulkhead). Use when designing or reviewing microservice architectures, or implementing distributed transactions, service discovery, or strangler fig migration.4license: MIT5---67# Microservices Architecture Pattern Rules89## 1. Service Decomposition Principles1011### Decomposition Criteria1213| Criterion | Description | Example |14| ------------------- | ----------------------------------------------------- | ------------------------------------ |15| Business Capability | Align services with organizational business functions | Order, Payment, Shipping, Inventory |16| Bounded Context | DDD aggregate boundaries define service scope | User ≠ Customer (different contexts) |17| Team Autonomy | One team owns one or more services end-to-end | Team can deploy independently |18| Data Ownership | Each service owns its data exclusively | Order DB, Payment DB separated |1920### Decomposition Rules2122- A service should be deployable and scalable independently23- A service should represent a single business capability or bounded context24- If two services always change together, they should be one service25- If a service requires deep knowledge of another service's internals, the boundary is wrong26- Start with fewer, coarser services — split only when complexity demands it2728### Right-Sizing a Service2930| Signal | Action |31| ---------------------------------------- | ------------------------ |32| Service has too many responsibilities | Split by business domain |33| Two services always deploy together | Merge into one |34| Team cannot understand the full codebase | Consider splitting |35| Service has only CRUD operations | May be too granular |36| Cross-service transactions are frequent | Reconsider boundaries |3738---3940## 2. Communication Patterns4142### Synchronous Communication4344| Protocol | Strengths | Weaknesses | Use Case |45| -------- | ---------------------------------- | ---------------------------------------- | --------------------------- |46| REST | Simple, ubiquitous, human-readable | Higher latency, no streaming | CRUD APIs, public-facing |47| gRPC | Fast, typed, streaming support | Requires proto definitions, less tooling | Internal service-to-service |4849### Asynchronous Communication5051| Pattern | Description | Use Case |52| ----------------- | ------------------------------- | ---------------------------------- |53| Message Queue | Point-to-point delivery | Task delegation, work distribution |54| Publish/Subscribe | Broadcast to multiple consumers | Event notification, data sync |55| Event Streaming | Ordered, replayable event log | Event sourcing, audit trail |5657### Communication Selection Criteria5859| Scenario | Recommended |60| ---------------------------------- | ------------------------ |61| Need immediate response | Synchronous (REST/gRPC) |62| Fire-and-forget operation | Async messaging |63| Multiple consumers need same event | Pub/Sub |64| Ordering and replay required | Event streaming |65| Long-running operation | Async + callback/polling |66| Cross-service data consistency | Saga via messaging |6768### Communication Patterns Rules6970- Prefer asynchronous communication between services — it reduces temporal coupling71- Use synchronous calls only when the caller needs an immediate response72- Never chain more than two synchronous calls — use async or aggregation instead73- Always set timeouts on synchronous calls74- Design messages to be self-contained — consumers should not need to call back to the producer7576### Event Publishing Example7778```kotlin79// Domain event80data class OrderCreatedEvent(81 val orderId: String,82 val userId: String,83 val totalAmount: BigDecimal,84 val items: List<OrderItem>,85 val occurredAt: Instant = Instant.now()86)8788// Publishing service89class OrderService(90 private val orderRepository: OrderRepository,91 private val eventPublisher: OrderEventPublisher92) {93 fun createOrder(request: CreateOrderRequest): Order {94 val order = orderRepository.save(Order.from(request))95 eventPublisher.publish(order.toCreatedEvent())96 return order97 }98}99```100101---102103## 3. API Gateway Pattern104105### Gateway Responsibilities106107| Responsibility | Description |108| -------------------- | ---------------------------------------------- |109| Routing | Route requests to appropriate backend services |110| Authentication | Validate tokens, enforce identity |111| Rate Limiting | Protect backends from traffic spikes |112| Response Aggregation | Combine responses from multiple services |113| Protocol Translation | REST to gRPC, WebSocket to HTTP, etc. |114| Load Balancing | Distribute traffic across service instances |115| Caching | Cache frequently requested responses |116117### Gateway Rules118119- The gateway should NOT contain business logic — only cross-cutting concerns120- Use a single gateway for external clients; consider per-client gateways (BFF) for different client types121- Rate limiting should be applied at the gateway level, not in each service122- Authentication should happen at the gateway; authorization should happen in individual services123- Circuit breakers at the gateway protect against cascading failures from unhealthy backends124125### Backend for Frontend (BFF) Pattern126127```text128Mobile App → Mobile BFF → Internal Services129Web App → Web BFF → Internal Services130Partner API → Partner BFF → Internal Services131```132133- Each BFF is tailored to its client's specific needs134- BFF aggregates and transforms backend responses for its client135- Avoids one-size-fits-all API that serves no client well136137### Anti-Patterns138139- Gateway becoming a monolith with business logic140- Single point of failure without redundancy141- Gateway performing data transformation that belongs in services142- Skipping the gateway for "internal" calls from external clients143144---145146## 4. Service Discovery147148### Discovery Models149150| Model | How It Works | Example |151| ----------- | ---------------------------------------------- | --------------------------- |152| Client-Side | Client queries registry, picks instance | Eureka + Ribbon |153| Server-Side | Load balancer queries registry, routes request | Kubernetes Service, AWS ALB |154| DNS-Based | Service registers DNS record, client resolves | Consul DNS, CoreDNS |155156### Comparison157158| Aspect | Client-Side | Server-Side | DNS-Based |159| -------------------- | ---------------- | ----------------- | ---------- |160| Client complexity | High (LB logic) | Low (transparent) | Low |161| Infrastructure needs | Service registry | LB + registry | DNS server |162| Health checking | Client-driven | LB-driven | TTL-based |163| Kubernetes native | No | Yes | Yes |164165### Service Discovery Rules166167- In Kubernetes environments, use native Service resources — no external registry needed168- For non-Kubernetes environments, use a dedicated service registry (Consul, Eureka)169- Always implement health checks — unhealthy instances must be removed from discovery170- Use DNS-based discovery for simplicity when advanced load balancing is not required171- Set appropriate TTL for DNS records to balance freshness and DNS load172173---174175## 5. Distributed Transactions (Saga Pattern)176177### Why Distributed Transactions178179- Each service owns its own database — no shared transactions across services180- Two-phase commit (2PC) has high latency, tight coupling, and poor availability — avoid it181- Saga pattern achieves eventual consistency through a sequence of local transactions182183> See [references/saga-patterns.md](references/saga-patterns.md) for detailed choreography vs orchestration comparison, examples, and compensating transaction table.184185### Saga Rules186187- Every forward action must have a corresponding compensating action188- Compensating actions must be idempotent — they may be invoked multiple times189- Design for eventual consistency — intermediate states are visible to users190- Use unique saga IDs for tracing the entire saga lifecycle191- Persist saga state to handle orchestrator failures and restarts192- Prefer choreography for simple flows; switch to orchestration when flows become complex193194---195196## 6. CQRS and Event Sourcing197198> See [references/cqrs-event-sourcing.md](references/cqrs-event-sourcing.md) for detailed CQRS architecture, event sourcing examples, and trade-offs.199200### CQRS and Event Sourcing Rules201202- CQRS and Event Sourcing are independent patterns — use one without the other203- Do not apply CQRS to the entire system — use it where read/write asymmetry exists204- Event Sourcing requires an event schema evolution strategy from day one205- Use snapshots to avoid replaying long event histories on every read206- Read model projections should be rebuildable from the event log at any time207208---209210## 7. Data Management211212### Database per Service213214```text215Order Service → Order DB (PostgreSQL)216Payment Service → Payment DB (PostgreSQL)217Search Service → Search Index (Elasticsearch)218Cache Service → Cache Store (Redis)219```220221### Data Ownership Rules222223- Each service owns its database exclusively — no other service accesses it directly224- Services expose data through APIs, not through shared database access225- Use events to propagate data changes to other services that need them226- Each service can choose the database technology best suited to its needs (polyglot persistence)227228### Data Synchronization Patterns229230| Pattern | Description | Consistency | Complexity |231| ------------------- | ------------------------------------------ | -------------- | ---------- |232| Event-Driven Sync | Publish events on change, consumers update | Eventual | Medium |233| Change Data Capture | Capture DB changes from transaction log | Near real-time | Medium |234| API Polling | Periodically fetch from source service | Delayed | Low |235| Dual Write | Write to both DB and event store | Risky | Low |236237### Transactional Outbox Pattern238239```kotlin240// Write to DB and outbox table in same transaction (transaction boundary)241fun createOrder(request: CreateOrderRequest): Order {242 val order = orderRepository.save(Order.from(request))243244 // Outbox entry — same transaction as business write245 outboxRepository.save(OutboxEntry(246 aggregateType = "Order",247 aggregateId = order.id,248 eventType = "OrderCreated",249 payload = objectMapper.writeValueAsString(order.toEvent())250 ))251252 return order253}254255// Separate process polls outbox and publishes to message broker256// After successful publish, mark outbox entry as published257```258259### Data Management Rules260261- Never use dual write (writing to DB and message broker separately) — it causes inconsistency on partial failure262- Use Transactional Outbox or Change Data Capture for reliable event publishing263- Accept eventual consistency — design UIs and APIs to handle intermediate states gracefully264- Shared Database is an anti-pattern — it creates tight coupling and prevents independent deployment265266---267268## 8. Fault Isolation269270### Resilience Patterns271272| Pattern | Purpose | Implementation |273| --------------- | ------------------------------------- | ------------------------------------ |274| Circuit Breaker | Stop calling a failing service | Resilience4j, Spring Circuit Breaker |275| Bulkhead | Limit concurrent calls per service | Thread pool isolation, semaphore |276| Retry | Retry transient failures with backoff | Spring Retry, Resilience4j |277| Timeout | Fail fast when service is slow | HTTP client timeout, `withTimeout` |278| Fallback | Provide degraded response on failure | Default value, cached response |279| Rate Limiter | Limit outbound request rate | Token bucket, sliding window |280281### Circuit Breaker States282283```text284CLOSED → (failure rate exceeds threshold) → OPEN285OPEN → (wait duration expires) → HALF_OPEN286HALF_OPEN → (test calls succeed) → CLOSED287HALF_OPEN → (test calls fail) → OPEN288```289290> See [references/migration-patterns.md](references/migration-patterns.md) for Resilience4j configuration example (circuit breaker, retry, bulkhead YAML).291292### Resilience Rules293294- Apply circuit breakers to all external service calls — no exceptions295- Set timeouts shorter than the caller's timeout to avoid cascading delays296- Use exponential backoff with jitter for retries — avoid thundering herd297- Retry only idempotent operations — or use idempotency keys for non-idempotent ones298- Bulkhead isolates failures — a slow service should not consume all threads299- Fallbacks should provide degraded but useful responses, not error pages300- Monitor circuit breaker state transitions — frequent OPEN states indicate systemic issues301302---303304## 9. Monolith to Microservices Migration305306> See [references/migration-patterns.md](references/migration-patterns.md) for Strangler Fig Pattern diagram and Branch by Abstraction code examples.307308### Migration Strategy Rules309310| Step | Action | Risk |311| ---- | ----------------------------------------------- | ------ |312| 1 | Identify bounded contexts in monolith | Low |313| 2 | Add API gateway in front of monolith | Low |314| 3 | Extract the least-coupled, highest-value domain | Medium |315| 4 | Migrate data to new service's database | High |316| 5 | Route traffic to new service via gateway | Medium |317| 6 | Decommission extracted code from monolith | Low |318| 7 | Repeat for next domain | Varies |319320### Monolith to Microservices Migration Rules321322- Extract one service at a time — never do a big-bang rewrite323- Start with the domain that has the most to gain from independent scaling or deployment324- Maintain backward compatibility during migration — old and new must coexist325- Use feature toggles to switch between monolith and microservice implementations gradually326- Data migration is the hardest part — plan for dual-write or CDC during transition327- Keep the monolith running until the extracted service is proven in production328329---330331## 10. Anti-Patterns332333### Distributed Monolith334335- Services are deployed independently but must be deployed together due to tight coupling336- **Symptoms**: changing one service requires changes in multiple other services; shared libraries with business logic; synchronous call chains337- **Fix**: enforce service boundaries through API contracts; eliminate shared domain libraries; use async communication338339### Excessive Service Decomposition340341- Too many fine-grained services create operational overhead without business benefit342- **Symptoms**: services with only 1-2 endpoints; most calls are service-to-service, not from clients; team manages more services than it can handle343- **Fix**: merge related services; apply the "two-pizza team" rule; split only when complexity demands it344345### Synchronous Call Chains346347- Service A calls B, B calls C, C calls D — latency compounds, availability drops exponentially348- **Symptoms**: response time is sum of all services; one slow service degrades the entire chain; cascading failures349- **Fix**: use async messaging; aggregate data at the gateway; cache intermediate results; use CQRS for read-heavy paths350351### Shared Database352353- Multiple services read from and write to the same database354- **Symptoms**: schema changes require coordinating multiple teams; database becomes the bottleneck; services cannot be deployed independently355- **Fix**: migrate to database-per-service; use events for data synchronization; accept eventual consistency356357### Missing Idempotency358359- Consumers process the same message multiple times with different results360- **Symptoms**: duplicate orders, double charges, inconsistent state after retries361- **Fix**: use idempotency keys; store processed message IDs; design consumers to handle duplicates362363### God Service364365- One service accumulates too many responsibilities and becomes a new monolith366- **Symptoms**: service has dozens of endpoints spanning multiple domains; multiple teams contribute to the same service; deployment is risky due to scope367- **Fix**: decompose by bounded context; enforce single responsibility at the service level368369---370371## 11. Related Rule References372373| Topic | Related Skill | Relevance |374| --------------------- | ------------------------ | ---------------------------------------------------- |375| Messaging patterns | `messaging` skill | Broker selection, producer/consumer patterns |376| API client resilience | `http-client` skill | Timeout, retry, circuit breaker configuration |377| Error handling | `error-handling` skill | Exception hierarchy, error response format |378| Monitoring | `observability` skill | Metrics, tracing, alerting for distributed systems |379| Caching | `caching` skill | Cache strategy, TTL, invalidation patterns |380| Database | `database` skill | Migration, transaction management, query patterns |381| API design | `api-design` skill | REST conventions, versioning, pagination |382| Security | `security` skill | Authentication, authorization, rate limiting |383| Logging | `logging` skill | Structured logging, traceId correlation |384| Spring implementation | `spring-framework` skill | RestClient, error handling, monitoring, Resilience4j |385386---387388## Additional References389390- For saga pattern implementation details and compensation strategies, see [references/saga-pattern.md](references/saga-pattern.md)391- For CQRS implementation patterns and event sourcing integration, see [references/cqrs.md](references/cqrs.md)392393## Further Reading394395- Sam Newman, *Building Microservices* (2nd Edition, O'Reilly)396- Chris Richardson, *Microservices Patterns* (Manning)397- Vaughn Vernon, *Implementing Domain-Driven Design* (Addison-Wesley)398- Martin Fowler's Microservices Resource Guide: <https://martinfowler.com/microservices/>399- Microsoft Azure Architecture Center — Microservices: <https://learn.microsoft.com/en-us/azure/architecture/microservices/>