Finding service boundaries
Good boundaries follow business decisions and change patterns; poor boundaries mirror database tables, UI screens, or organisational history.
Model capabilities, not nouns
An Order service is not valuable because an orders table exists. It is valuable when order placement, pricing commitments, status transitions, and cancellation rules form a cohesive decision-making unit.
Use domain language with product specialists. Look for rules that change together, terms with precise meanings, and workflows with a clear owner. Domain-driven design calls these bounded contexts; the practical point is to stop one model from pretending to mean the same thing everywhere.
Use change coupling as evidence
Study recent work. Files, tables, and teams that repeatedly change together probably belong together. A proposed boundary that forces most features to cross three services is fighting the product rather than separating it.
Runtime calls are only one form of coupling. Shared releases, shared databases, synchronized schema changes, and a central team approving every modification can produce a distributed monolith even when each box has its own deployment.
- High cohesion inside the service
- Few synchronous calls on common paths
- Minimal shared data
- A clear owner
- A contract expressed in domain language
Extract with a strangler path
Choose one capability with a clear seam. Route new behaviour to the new service while the old application continues serving everything else. Move authority deliberately, observe production behaviour, and remove the old path only after consumers have migrated.
Avoid copying a table and declaring both copies authoritative. During migration, define which system accepts writes and how other representations catch up.
Map ownership before APIs
Recognise boundaries that are too small
If two services always deploy together, share every incident, and cannot answer a request without each other, the split has not created autonomy. Merge them or move the boundary until each side owns a useful outcome.
A service per entity, CRUD endpoint, or engineer maximises operational surface while preserving the original coupling. Prefer fewer, richer services.
Can you explain “Finding service boundaries” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.