Service Decomposition β DDD, Strangler Fig & Data Decoupling
Service Decomposition is the architectural practice of dissecting a large software monolith into distinct, cohesive, and independently deployable microservices.
Decomposing along arbitrary technical boundaries (e.g. by database tables or technical tiers) is the leading cause of microservice failure, culminating in a Distributed Monolithβan architecture that inherits all the latency, network unreliability, and operational complexity of distributed systems while forfeiting independent deployments.
To succeed at scale, principal engineers apply Domain-Driven Design (DDD) Context Mapping, the Strangler Fig Pattern, and progressive database decoupling strategies.
1. Bounded Contexts & Domain-Driven Design (DDD)
The primary tool for establishing resilient service boundaries is Domain-Driven Design. Rather than carving services along database schemas, services must map to business Bounded Contexts:
1.1 The Ubiquitous Language & Polysemic Entities
A single business concept often holds conflicting definitions across different business departments. Attempting to create a universal, unified "User" or "Order" class across the entire enterprise creates an unmaintainable god-class.
In DDD, entities are polysemic (having different meanings in different contexts):
| Business Entity | Ordering Context | Fulfillment / Shipping Context | Billing / Invoice Context |
|---|---|---|---|
| "Order" | Shopping cart items, applied coupons, customer checkout ID | Weight, volume, shipping pallet dimensions, tracking number | Tax jurisdiction, currency exchange, payment gateway token |
| "User" | PII name, delivery address, communication preferences | Recipient address, gate code, dispatch notes | Tax ID, credit card token, legal invoicing entity |
Each Bounded Context maintains its own distinct domain model, class definitions, and database storage.
1.2 Context Mapping Patterns
When microservices communicate across boundaries, the relationship between contexts must be explicitly classified:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DDD CONTEXT MAPPING ARCHITECTURES β
β β
β UPSTREAM (Core Domain) DOWNSTREAM (Supporting Domain) β
β ββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ β
β β Monolithic Core System β β Modern Cloud Microserviceβ β
β β Complex, Legacy Model β β Clean Domain Model β β
β ββββββββββββββ¬ββββββββββββββ ββββββββββββββ²ββββββββββββββ β
β β β β
β β Proprietary Wire Format β Clean DTOs β
β βΌ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββ β
β β ANTICORRUPTION LAYER (ACL) β β
β β Translates legacy monolith tables/APIs into clean downstream domain DTOs.β β
β β Prevents legacy monolithic baggage from leaking into the new service. β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Anticorruption Layer (ACL): A translation adapter placed between the downstream new service and upstream legacy system to translate data models, preventing the new service's domain model from being polluted.
- Open Host Service (OHS) / Published Language (PL): The upstream service exposes a standardized, versioned public protocol (e.g. gRPC with Protocol Buffers or OpenAPI JSON) rather than allowing callers to read internal database formats.
- Customer / Supplier: Upstream (Supplier) delivers features required by Downstream (Customer), with planning negotiated in lockstep.
2. Refactoring Playbook: The Strangler Fig Pattern
Never attempt a "Big Bang" rewrite. A complete rewrite of an enterprise monolith almost always runs over budget, fails to achieve feature parity, and risks catastrophic production regressions.
The standard industry approach is the Strangler Fig Pattern (incrementally replacing monolithic capabilities until the monolith has withered away):
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STRANGLER FIG MIGRATION PIPELINE β
β β
β CLIENT TRAFFIC β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β EDGE API GATEWAY / REVERSE PROXY (Envoy / Kong / AWS ALB) β β
β β Routes by Path, Feature Flag, or Percentage Weight β β
β ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ β
β β β β
β Legacy Routes β β Migrated Routes β
β (e.g. /cart) β β (e.g. /payments) β
β βΌ βΌ β
β βββββββββββββββββββββββββ CDC / Event βββββββββββββββββββββββββ β
β β LEGACY MONOLITH β βββββββββββββ> β EXTRACTED MICROSERVICEβ β
β β Monolithic Database β (Debezium) β Isolated Microservice β β
β β (Legacy Relational) β β Database (Postgres) β β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The 4 Migration Phases
- Phase 1: Intercept & Shadow Traffic (Dark Launching):
- Deploy an API gateway in front of the monolith.
- Extract the target domain logic into the new microservice.
- Route production traffic to the monolith, but asynchronously mirror (
shadow) the request payload to the new microservice. Compare execution outputs, latency, and edge cases with zero client impact.
- Phase 2: Dual-Writing with Change Data Capture (CDC):
- Synchronize database state between the legacy database and the new service's database using tools like Debezium reading Postgres/MySQL Write-Ahead Logs (WAL).
- Phase 3: Write Cut-Over:
- Flip the API Gateway routing to direct 100% of live write traffic to the new microservice.
- Reverse the CDC stream: stream new microservice writes back to the legacy database to keep legacy reporting systems and batch jobs functioning.
- Phase 4: Monolithic Deletion:
- Sever the reverse CDC stream.
- Delete the legacy code paths from the monolith repository.
3. Database Extraction: Managing Distributed Data Fallout
Extracting application code into microservices is straightforward; extracting the database is where systems break down.
3.1 The Foreign Key Trap
In a monolith, referential integrity is enforced at the database engine level via Foreign Keys (FOREIGN KEY (customer_id) REFERENCES customers(id)). When CustomerService and OrderService are split into separate physical databases, foreign keys across databases are impossible.
Architectural Solutions:β
- Application-Level Referential Validation:
OrderServicevalidatescustomerIdvia an internal gRPC call or an in-memory Redis cache populated fromCustomerServicechange events.
- Eventual Consistency with Tombstone Records:
- If a customer is deleted in
CustomerService, it publishes aCustomerDeletedEventover Kafka. OrderServiceconsumes the event and marks orders as belonging to an archived/anonymized customer rather than failing with cascading delete constraints.
- If a customer is deleted in
3.2 Cross-Service Queries: API Composition vs. CQRS
How do you display a screen showing "Orders with Customer Name and Delivery Tracking Status"?
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CROSS-SERVICE QUERY ARCHITECTURES β
β β
β APPROACH A: API COMPOSITION (Frontend / BFF Level) β
β Client / BFF calls OrderService -> CustomerService -> ShippingService sequentially. β
β β’ Pros: Simple to implement. β
β β’ Cons: High network fan-out; latency is the sum of all downstream calls; β
β vulnerable to cascading failures if ShippingService goes down. β
β β
β APPROACH B: CQRS (Command Query Responsibility Segregation) β
β OrderService, CustomerService, and ShippingService emit Kafka domain events. β
β A dedicated "OrderViewProjectionService" consumes these events and updates a denorm- β
β alized read-optimized document store (e.g. Elasticsearch / MongoDB). β
β β’ Pros: Sub-10ms single-query read; resilient to downstream outages. β
β β’ Cons: Eventual consistency lag; complex projection rebuilding pipelines. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
4. The Distributed Monolith: Warning Signs & Diagnostic Matrix
If services are decomposed along the wrong fault lines, the organization ends up with a distributed monolith.
| Anti-Pattern Indicator | Technical Root Cause | Architectural Remediation |
|---|---|---|
| Lockstep Deployments | Service A cannot deploy without releasing Service B at the same minute. | Breaking API contracts; establish semantic versioning and consumer-driven contract tests (Pact). |
| Shared Database Tables | Multiple microservices connect to the same relational DB tables. | Violates data ownership; enforce strictly isolated database instances per service. |
| Deep Synchronous Call Chains | Service A calls B, which calls C, which calls D, resulting in 500ms latency. | Temporal coupling; transition to asynchronous event-driven choreographies via Kafka/RabbitMQ. |
| Excessive Distributed Transactions | Frequent cross-service rollbacks requiring complex 2PC logic. | Boundaries are too granular; merge the tightly coupled services back into a single bounded context. |
5. Organizational Alignment: Conway's Law & Team Topologies
System architecture inevitably mirrors an organization's communication structures (Conway's Law). Attempting to deploy 30 fine-grained microservices with a team of 4 engineers creates unmanageable cognitive load and operational burnout.
Team Topologies Framework
When decomposing services, organize engineering around 4 fundamental team types:
- Stream-Aligned Teams: Dedicated to a continuous flow of work in a single business domain (e.g. Checkout, Onboarding). They own their microservices from cradle to grave ("You build it, you run it").
- Platform Teams: Provide underlying internal infrastructure (e.g. Kubernetes, observability, CI/CD pipelines, service meshes) so stream-aligned teams do not reinvent plumbing.
- Enabling Teams: Cross-functional specialists (e.g. security architects, performance engineers) who upskill stream-aligned teams on new practices.
- Complicated-Subsystem Teams: Formed only when rare domain expertise is required (e.g. algorithmic video transcoding, mathematical options pricing engines).
