Why do most microservices practitioners insist that service boundaries be drawn around business/bounded contexts rather than around technical layers such as 'all validation logic' or 'the notification tier'?
answer
- Order means different things per context
- capability not layer
- shared validation/notification service = hidden coupling
- DDD context boundaries
- duplication is the price of decoupling
basics
~20 sGrouping a service by business area (like 'Payments' or 'Inventory') keeps everything that changes together in one place. Splitting by technical layer instead means one small business change touches many services at once, which is slow and fragile.
solid answer
~40 sA bounded context (from Domain-Driven Design) is a boundary within which a specific domain model and vocabulary stay consistent -- 'Customer' can mean something different and be modeled differently in Sales versus Support. Drawing service boundaries along bounded contexts means each service maps to a cohesive unit of business capability that changes for one reason, so most business changes touch exactly one service. Drawing boundaries along technical layers instead (a shared validation service, a notification service used by everything) means a single business feature change ripples across several services and requires coordinated deploys, reintroducing the coupling microservices are meant to remove. It also aligns with team ownership: a team can own a business capability end to end (API, logic, data) rather than owning a thin technical slice every feature depends on.
go deeper
Should be able to say, in plain terms, that a service should represent a business area, not a technical piece like 'validation,' and give a rough example.
Should define bounded context correctly, explain why the same word (e.g., Order) can have different models in different contexts, and identify a technical-layer split as an anti-pattern by its symptoms.
Should be able to evaluate a real service boundary proposal, spot when it's actually a disguised technical layer, and explain the accepted trade-off of code duplication in exchange for deployment independence.
Should be able to guide an org through re-drawing boundaries when they were originally set by technical layer, including the migration risk and how to sequence it without a big-bang rewrite.
## What a bounded context is A **bounded context** is a Domain-Driven Design concept: it's the boundary -- often a subsystem, module, or team's area of responsibility -- within which a particular domain model and its vocabulary are guaranteed to be internally consistent. The same real-world word can mean genuinely different things on either side of that boundary. In an e-commerce system: - 'Order' inside the context that handles **cart checkout** might be a simple line-item list with a total. - 'Order' inside the context that handles **warehouse fulfillment** might be a set of pick-and-pack tasks with shipping addresses and carrier assignments. - 'Order' inside the context that handles **finance** might be a set of ledger entries and tax lines. These are not three views of one canonical Order -- treating them as one shared model forces awkward compromises where every context carries fields it doesn't need and breaks when another context's requirements change. Bounded contexts formalize the idea that each of these is its own model, valid within its own boundary, with explicit translation at the seams where contexts talk to each other. ## Capability boundaries versus technical layers When a microservices architecture draws its service boundaries around bounded contexts, each service becomes the technical home for one cohesive slice of business capability: an Order-Fulfillment service, a Billing service, a Catalog service. The alternative is organizing services by technical layer, e.g., a shared Validation service every other service calls to validate any input, or a Notification service every feature routes its emails through. That sounds appealing because it avoids 'duplicating' validation or notification logic. In practice it inverts the coupling microservices are meant to reduce. A single business change, like adding a new required field to the checkout flow, now has to touch the Checkout service and the shared Validation service and possibly the Notification service, and all three have to be compatible in the same release for the feature to work. That is close to the deployment coupling of a monolith, except now it also has network calls and separate failure modes between the layers -- worse in most ways, not better. ## The trade-off The trade-off is not free even when done well. - **Duplication is the price.** Business-capability boundaries mean each service will duplicate some logic that a shared technical-layer service would have centralized -- every service that needs input validation implements or imports its own, every service that sends notifications calls out to a notification capability on its own terms. That duplication is the price paid for decoupling: it trades a small amount of repeated code for the much larger win of not needing synchronized deploys across services for routine business changes. - **Getting the boundary right is work.** It also requires real domain modeling work up front, and a boundary drawn wrong -- **too coarse**, so unrelated concerns pile into one service and start needing internal technical sub-splits again; or **too fine**, so a single business capability is chopped into pieces that always change together -- tends to surface later as either a bloated 'god service' or a set of chatty services behaving like a distributed monolith. ## The failure mode in production The failure mode most visible in production is the technical-layer split described above: teams notice that a shared 'utility' service -- auth checks, notification, search indexing -- has become a bottleneck, because every other team's feature work depends on that team's release cadence and API stability. Postmortems on these often read like monolith incidents: one service's bug or slow deploy blocks unrelated feature teams, even though the org has dozens of nominally separate services. The fix is usually one of these: 1. Push the capability back down into each bounded-context service (accepting the duplication). 2. Or make the shared capability genuinely infrastructure -- a library each service embeds and owns its own copy of, rather than a runtime dependency every request has to call through. ## Where the idea comes from A well-known real-world illustration is how Amazon's internal services are organized: capabilities like cart, checkout, and recommendations are each owned end-to-end by a team that models its own version of the relevant domain concepts and exposes them through a versioned interface, explicitly avoiding a shared internal database or a shared technical-layer service every team would have to coordinate through. This lines up with Eric Evans' original Domain-Driven Design framing, where bounded contexts were proposed specifically to stop large systems from collapsing under the weight of one all-purpose shared model that every team has opinions about and every team's changes destabilize.
- What is a concrete symptom that a system has drawn service boundaries around technical layers instead of bounded contexts?A single, ordinary business feature change requires coordinated deploys across several services -- for example, a shared validation or notification service that every feature team depends on and must schedule releases around. If most feature work ripples across three or more services instead of staying inside one, that's the tell.
- Does organizing around bounded contexts eliminate the need for any shared code between services?No. Cross-cutting infrastructure concerns like logging conventions, auth token validation, or a metrics client are still commonly shared, but typically as a library each service vendors and owns its own version of, rather than as a runtime service every request must call through -- that keeps the sharing from becoming a deployment dependency.
- How does getting a bounded-context boundary wrong tend to surface later?Too coarse and a service accumulates unrelated concerns that start needing their own internal technical sub-splits, becoming a 'god service' that's hard to reason about. Too fine and you get services that almost always change together and call each other constantly for basic operations, which behaves like a distributed monolith with extra network overhead.
Like organizing a company into product divisions (each with its own sales, engineering, support) rather than into company-wide functional silos (one giant sales department serving every product) -- each division can move at its own pace instead of queuing behind a shared department.
saying these in an interview costs you the question
- proposes a shared 'utility service' for validation/notifications as good practice
- doesn't know what a bounded context is
- thinks bounded contexts just mean 'microservice = database table'
- believes eliminating all logic duplication across services is a goal
- can't explain why a shared model breaks down across business areas