Service Decomposition & Bounded Contexts (DDD)
Cut along business capabilities and bounded contexts, give every service exclusive ownership of its data (API/event access only, never a shared table), size services to one team each, and extract incrementally with the Strangler Fig and an anti-corruption layer.
Where to cut is the hard part
Once you decide to split, the hard part is where to cut. Bad boundaries are what turn microservices into a distributed monolith. The tool for drawing good boundaries is Domain-Driven Design, specifically the bounded context.
Align boundaries to business capabilities
The core rule: align service boundaries to business capabilities, not technical layers. A common novice split is by layer (a UI service, a business-logic service, a data service), which is disastrous, because almost every feature touches all three, so every change requires coordinated deploys across all of them. Instead you split by capability: Orders, Payments, Inventory, Shipping, Catalog, Identity. Each maps to a bounded context, a part of the domain with its own model and its own language. The word "product" means an SKU with price and description in Catalog, but a line item with quantity and fulfillment status in Orders. Those are different models and belong in different services.
Interview nuance: the single most important consequence of a bounded context is data ownership. Each service owns its data and no other service touches its database. Access is only through the owning service's API or through events it publishes. If two services share a database table, they are coupled at the schema level: you cannot change the table without coordinating both deploys, and you are back to a distributed monolith. Interviewers will test this directly: "the Shipping service needs the customer's address, where does it get it?" The right answer is it calls Identity's API or subscribes to a customer-updated event and keeps its own copy, never a join across databases.
Right-sizing in both directions
Nano-services (one service per table or per endpoint) create chatty networks where a single user action fans out to 20 synchronous calls, and latency and failure probability compound. God-services swallow half the domain and become a monolith with a network in front. The heuristic: one team owns each service, and a service should be independently deployable and understandable by that team. This is Conway's Law used deliberately, the Inverse Conway Maneuver: design the team structure and the service structure together, because your architecture will end up mirroring your org chart whether you plan it or not.
Extract with the Strangler Fig
You rarely rewrite. The standard extraction pattern is the Strangler Fig. You put a routing layer (an API gateway or proxy) in front of the monolith, then peel off one capability at a time: stand up the new Payments service, route payment traffic to it, and leave everything else in the monolith. Over months you strangle the old code path until the monolith is gone or reduced to a small core. At each seam you place an anti-corruption layer, a translation shim that maps the monolith's messy legacy model to the new service's clean model, so the old design does not leak into and corrupt the new one.
Stage 1 of 3: You rarely rewrite. First move: put a routing layer (an API gateway or proxy) in front of the monolith. All traffic still flows to the legacy code, but you now own the seam.
Recap: cut along business capabilities and bounded contexts, give every service exclusive ownership of its data, size services to one team each, and extract incrementally with the Strangler Fig and an anti-corruption layer.
Apply
Your turn
The task this lesson builds to.
Produce a service map for a monolithic e-commerce app: name the bounded contexts, their data ownership, and the two seams you would cut first.
Think about
- Why align boundaries to business capabilities, not technical layers?
- Why must each service own its data (no shared DB)?
- How does the Strangler Fig pattern extract incrementally?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design the decomposition and 18-month extraction plan for a large monolith like early Uber's, where the rider, driver, trip, pricing, and payments logic all live in one Python service and one Postgres, and the org is about to grow from 3 to 15 teams. Lead with the deliverable.
Think about
- Which context has the sharpest extraction trigger, and why extract it first?
- How does the Inverse Conway Maneuver sequence team and service creation?
- What consistency tradeoff do you accept for real-time matching?