Microservices Decomposition Strategies
Decomposing a monolithic application into microservices requires careful analysis of business domains, data ownership, and team structures. Poor decomposition leads to distributed monoliths that inherit the complexity of both architectures without the benefits of either.
Domain-Driven Design (DDD) provides the foundational approach for decomposition. Identify bounded contexts, which define clear boundaries where a particular model applies. Each bounded context becomes a candidate microservice. For example, in an e-commerce platform, Order Management, Inventory Management, and Payment Processing are distinct bounded contexts with their own data models and business rules.
Decomposition by Business Capability aligns microservices with organizational functions. Business capabilities describe what the organization does: process orders, manage inventory, handle customer support. Map each capability to a microservice with full ownership of its data and logic. This approach aligns with Conway's Law, where system architecture mirrors organizational structure.
Decomposition by Subdomain from DDD categorizes subdomains as core, supporting, or generic. Core subdomains provide competitive advantage and deserve dedicated, well-designed microservices. Supporting subdomains are necessary but not differentiating, suitable for simpler implementations. Generic subdomains like authentication or email can use off-the-shelf solutions or shared services.
Data Decomposition is often the hardest part. Each microservice should own its data store. This eliminates tight coupling through shared databases but introduces challenges with data consistency and cross-service queries. For an order management service, the orders database belongs exclusively to that service. Other services needing order data must access it through the order service's API.
Strangler Fig Pattern provides a safe migration approach. Instead of rewriting the monolith completely, gradually replace monolith functionality with microservices. Route new feature requests to microservices while the monolith handles existing features. Over time, the monolith shrinks as more functionality moves to microservices. A banking platform might first extract the notification service, then the authentication service, gradually decomposing the monolith over 12-18 months.
Team Topologies should match microservices boundaries. Each microservice should be owned by a small, autonomous team (typically 5-8 people). The team has end-to-end responsibility for their service including development, testing, deployment, and operations. This ownership model enables fast decision-making and reduces coordination overhead.
API Gateway, Service Mesh, and Resilience Patterns
Microservices architectures require patterns for communication, resilience, and observability. These patterns address the complexity challenges that distributed systems introduce.
API Gateway Pattern provides a single entry point for all client requests. The gateway handles cross-cutting concerns like authentication, rate limiting, request routing, and response aggregation. Amazon API Gateway offers managed API gateway functionality with built-in throttling, caching, and authorization. For a mobile application, the API Gateway might aggregate data from five microservices into a single response, reducing network round trips and simplifying client code.
Service Mesh Pattern manages inter-service communication through a dedicated infrastructure layer. AWS App Mesh provides service-to-service communication with traffic routing, monitoring, and security. The mesh uses sidecar proxies (Envoy) deployed alongside each microservice to handle communication concerns transparently. This pattern enables features like traffic splitting for canary deployments, circuit breaking, and distributed tracing without modifying application code.
Sidecar Pattern deploys helper components alongside primary application containers. The sidecar handles logging, monitoring, networking, and security functions. In EKS deployments, a common sidecar collects application logs and sends them to CloudWatch while the main container focuses on business logic. This separation of concerns keeps microservices focused and simplifies operations.
Circuit Breaker Pattern prevents cascading failures when a downstream service is unavailable. The circuit breaker monitors requests and opens the circuit when failure rates exceed thresholds, returning fallback responses immediately. After a timeout, it allows test requests to determine if the service has recovered. Hystrix and AWS App Mesh implement circuit breaking. For example, if the recommendation service fails, the product page displays cached recommendations instead of waiting for timeouts.
Saga Pattern manages distributed transactions across microservices. Instead of a single ACID transaction spanning multiple services, sagas use a sequence of local transactions with compensating actions for rollback. The Choreography approach has each service publish events that trigger the next step. The Orchestration approach uses a central coordinator. An order saga might: reserve inventory, process payment, create shipment. If shipment fails, compensate by refunding payment and releasing inventory.
CQRS (Command Query Responsibility Segregation) separates read and write models. Commands modify state through the write model optimized for consistency. Queries read from a read model optimized for query performance, often using denormalized views. This pattern is particularly valuable when read and write workloads have different scaling requirements. A social media feed service might use a write model for posts and a separate read model optimized for feed queries.
Quiz
1. What is the primary purpose of an API Gateway in microservices?
2. What pattern prevents cascading failures when a downstream service is unavailable?
3. In Domain-Driven Design, what is a bounded context?
4. How does the Saga pattern handle distributed transaction failures?
Flashcards
Question
What is Domain-Driven Design bounded context?
Click to reveal answer
Answer
A clear boundary within which a specific domain model applies. Each bounded context becomes a candidate microservice boundary.
Question
What is the Circuit Breaker pattern?
Click to reveal answer
Answer
A resilience pattern that monitors downstream service failures and opens the circuit to return fallback responses, preventing cascading failures.
Question
What is CQRS?
Click to reveal answer
Answer
Command Query Responsibility Segregation separates read and write models, optimizing each for its specific workload characteristics.
Question
What is the Strangler Fig pattern?
Click to reveal answer
Answer
A migration pattern that gradually replaces monolith functionality with microservices, allowing incremental decomposition without a complete rewrite.
Revision Notes
Key Takeaways
- 1. Use Domain-Driven Design bounded contexts to identify natural microservice boundaries
- 2. Each microservice should own its data store to avoid tight coupling
- 3. Circuit breaker and saga patterns address distributed system reliability challenges
- 4. API Gateway and service mesh handle cross-cutting communication concerns
Interview Tips
- • Explain how you would decompose a monolithic e-commerce application into microservices
- • Discuss the trade-offs between Choreography and Orchestration sagas
- • Describe when you would use CQRS and the performance benefits it provides
- • Walk through how circuit breaker prevents cascading failures with a real example
Cheat Sheet
Decomposition: DDD bounded contexts, business capabilities, subdomain analysis. Patterns: API Gateway (entry point), Service Mesh (sidecar proxies), Circuit Breaker (cascading failure prevention), Saga (distributed transactions via compensations), CQRS (separate read/write models). Strangler Fig for gradual migration.