Bounded context is where most developers stall out in the DDD book. The concept is abstract, and Evans’s examples are dated (shipping companies, navy fleet management). Few resources show you how to actually draw the line in practice.
Across the SaaS architectures I’ve built in the last four years, I lean on four concrete criteria to draw bounded-context boundaries. Here’s each one with examples.
1. Linguistic boundary
If the same word means different things to different departments, a bounded context lives on that seam.
Take “Product” in an e-commerce system:
– Inventory: SKU, stock level, warehouse location
– Catalogue: name, description, images, category
– Orders: price at purchase time, quantity, discount
– Shipping: weight, dimensions, shipping class
– Accounting: cost price, revenue, VAT rate
Each one has different fields and different business rules. Try to force everything into a single Product class and you end up with a 50-field god object that isn’t the right abstraction for any of those contexts.
The right move: every context gets its own view of Product. Inventory, catalogue, and orders are independent models. Data flows between contexts via events or APIs.
How to test it: in a meeting, does everyone mean the same thing by this word? If teams say “from our perspective, Product is actually something else”, that’s your boundary.
2. Transactional boundary
Inside a single atomic transaction, what pieces of data have to change together? That question gives you an aggregate boundary, and groups of aggregates that live together give you a bounded context.
Order placement is a good example. In one user action:
– Create the Order record (Order context)
– Authorize payment (Payments context)
– Reserve stock (Inventory context)
– Calculate loyalty points (Customer context)
These four steps do not all belong in one transaction. Order creation is synchronous, the rest can be event-driven. Each lives in its own bounded context, eventually consistent.
How to test it: ask “do these two tables always have to be updated together?” Yes, same context. “Usually, but sometimes with a delay”, separate contexts.
3. Team boundary
Conway’s law: the organisation shapes the code. If different teams own different responsibilities, the codebase should reflect that split.
On a real project we had three teams:
– Growth: signup, onboarding, email marketing
– Product: core features, dashboard
– Billing: subscriptions, invoices, tax
The three teams already had separate standups and different velocities. We split the codebase along those same lines. Each team got its own bounded context, its own repo, and its own deploy cycle.
The result: teams could deploy without blocking each other, code review stopped being a bottleneck, and the on-call rotation got fairer.
How to test it: “who builds this feature?” Is the answer one clear team, or “a few teams together”? The second answer is a bounded-context problem.
4. Data ownership
Should every bounded context have its own database? Ideally yes, but even on a shared DB you can draw clear ownership lines.
The rule: only one context writes to a given table. Other contexts can read it or keep a local copy in their own cache, but they don’t write.
For instance: only the Auth context writes the users table. When the Order context needs user data it either reads via foreign key (in a monolith) or copies it over via events into its own database (in microservices).
Break this rule and migrations become terrifying. Two or three services all writing the same columns, and “who uses this column?” becomes a 15-minute investigation.
How to test it: label every table with its owning context. Then check every bit of code that writes to it. Does only the owner write?
How to start in practice
Designing a new system, walk through this order:
- Map the business domain with an event-storming workshop. Every business event on a sticky note.
- Group events that share a common language. Those are your candidate contexts.
- For each context, answer “who owns it?” Does it line up with your team structure?
- Design the inter-context communication. Which events get published, which APIs get exposed?
Those four steps usually take two or three days, and they pay back hugely over a two-year build.
The cost of drawing the line wrong
A bad bounded-context boundary is one of the most expensive things to fix later. Data sits in the wrong place, teams step on each other’s code, deploys wait on each other. It can turn into a 3 to 6 month refactor.
An extra day or two up front saves all of that. The frame: language, transaction, team, data ownership. Decide through those four lenses.