Two teams building an e-commerce platform both use the word 'Order' but mean different things — one team's checkout service treats an Order as a cart snapshot awaiting payment, another team's warehouse-fulfillment service treats an Order as a pick list of physical items. How should the shared domain vocabulary be handled when the same word carries different meanings in different parts of the system?
answer
- language holds within one bounded context only
- context map names the boundaries
- anti-corruption layer translates at the seam
- shared kernel for the few things truly shared
- don't merge models across team boundaries
basics
~20 sDon't force one meaning of 'Order' everywhere. Let each service keep its own precise definition inside its own boundary, and put a clear translation step where the two services talk to each other, so nobody assumes a word means the same thing on both sides.
solid answer
~50 sA single ubiquitous language only holds inside one bounded context — a subsystem with a consistent model and team ownership. When two contexts use the same word for different concepts, the fix isn't to force a single company-wide definition; it's to accept that 'Order' in Checkout and 'Order' in Fulfillment are different models that happen to share a spelling, document that on a context map, and translate explicitly at the integration boundary (e.g., an anti-corruption layer or a dedicated integration/translation service) rather than passing the same DTO or entity between them untranslated. Each team keeps refining its own language internally with its own domain experts. The trade-off is extra integration work — mapping code, careful API contracts, sometimes duplicated data — in exchange for each context staying internally coherent instead of being forced into a lowest-common-denominator model that satisfies neither team well.
go deeper
Can recognize that the same word meaning different things in different services is confusing and should be pointed out; not expected to know terms like bounded context or anti-corruption layer yet.
Knows the concept of a bounded context and proposes keeping each team's model separate with some translation at the boundary, even if the exact pattern name is fuzzy.
Names and correctly applies patterns like anti-corruption layer, shared kernel, and context map; can decide which pattern fits a given integration and explain the coupling trade-off.
Uses language divergence as an architectural signal to decide where service/team boundaries should be drawn org-wide, and sets conventions (context mapping practice, ACL ownership rules) that keep this manageable across dozens of contexts.
## The language holds inside one boundary Ubiquitous language is only guaranteed to be consistent within a **bounded context** — a boundary (often aligned to a team, a service, or a subdomain) inside which a single model and a single vocabulary apply without contradiction. Outside that boundary, DDD explicitly expects language to diverge, because different parts of a business genuinely think about the same real-world thing differently. The mechanism for handling this is not to suppress the divergence but to make it visible and managed: teams draw a **context map**, a diagram or document that names each bounded context and describes the relationship and translation strategy at each place two contexts meet. For the checkout-versus-fulfillment example, the context map would show two contexts connected by an explicit integration point: | Context | Whose 'Order' means | |---|---| | **Checkout** | 'a priced, payment-pending snapshot of a cart' | | **Fulfillment** | 'a set of physical line items with locations and pick instructions' | ## Patterns applied at the integration point At that integration point, teams typically apply one of a few well-known patterns. - **An anti-corruption layer (ACL)** is a translation module owned by the downstream context that converts the upstream context's model into its own local vocabulary and shape before anything crosses the boundary — so Fulfillment never directly consumes Checkout's `Order` object; it consumes a `FulfillmentOrder` that its own ACL constructs from whatever Checkout sends. - **Alternatively, a published language or shared kernel** — teams can agree on one for the narrow slice of concepts they truly need to agree on (e.g., a shared `OrderId` type and a small set of fields), while keeping everything else local and independent. The point of all these patterns is the same: **never let one team's internal model leak untranslated into another team's context**, because that silently welds two models together that were never designed to be one. ## Why one universal model is a design failure The reason this exists is that forcing one universal model onto a whole organization is a design failure, not a design win. Early or overambitious DDD adoption often tries to build 'the one true `Order` class' shared across every service, reasoning that consistency reduces confusion. In practice this produces an anemic, compromise model that satisfies no team's actual needs — Checkout needs fields Fulfillment doesn't care about and vice versa, and every change to the shared model requires cross-team negotiation and coordinated deployment, which kills velocity. Recognizing that 'Order' legitimately means two things is what lets each team optimize its own model for its own concerns without waiting on the other. ## The trade-off The trade-off is real **integration cost**: - translation code has to be written and maintained, - data sometimes has to be duplicated or eventually-consistent across contexts (e.g., Fulfillment keeps its own copy of order data rather than querying Checkout's database live), - new engineers have to learn that the same English word means different things depending on which service they're reading. Teams that skip this discipline get a false sense of savings up front and pay it back later when a schema change in one context silently breaks another context that was reading its raw model. ## Failure modes Failure modes are easy to spot in production. 1. **The most common is 'shared database, no translation'**: two services read and write the same `Order` table directly, so a schema change made for Checkout's needs (e.g., adding a payment-retry field) silently breaks a report or process in Fulfillment that assumed the old shape — an implicit, undocumented coupling caused by skipping the ACL. 2. **Another failure mode is a context map that was drawn once and never updated**, so new integrations get built ad hoc without anyone deciding whether they need translation, and the number of untranslated cross-context dependencies grows until nobody can change either service safely. 3. **A third is over-applying translation everywhere**, even between two sub-teams that genuinely share one small subdomain, which adds needless boilerplate — the skill is judging when contexts are truly semantically different versus just organizationally separate. ## Sales versus Support, the classic case A concrete real-world pattern is the classic DDD 'Sales' versus 'Support' example: | Bounded context | What a 'Customer' is | |---|---| | **Sales** | a lead with a sales-pipeline stage and quota attribution | | **Support** | an account with open tickets and an SLA tier | Both are legitimately called 'Customer', both are correct within their own context, and a CRM integration layer translates between the two — pulling a sales lead's contact info into a support account record rather than trying to merge them into one shared `Customer` class.
- What's the difference between a shared kernel and an anti-corruption layer?A shared kernel is a small, deliberately agreed-upon slice of model that two contexts both use directly and change together, useful when the overlap is genuinely small and stable, like a common identifier type. An anti-corruption layer instead assumes the two contexts' models should stay fully independent and puts translation code at the boundary so neither side has to change when the other does; it's the more common and more defensive choice when teams don't want tight coupling.
- How do you decide where a bounded context boundary should be drawn in the first place?Language divergence is one of the strongest signals — if two groups of domain experts use the same word to mean different things, or use different words for what turns out to be the same thing, that's evidence a context boundary already exists conceptually, whether or not it's reflected in the code or team structure yet. Boundaries are also informed by team ownership, deployment independence, and rate of change, but language mismatch is usually the first clue engineers notice.
- What goes wrong if two teams skip translation and just share one database table for 'Order'?The two teams end up implicitly coupled through the schema — a column added for Checkout's payment-retry logic can silently break a report or a batch job in Fulfillment that assumed the old row shape, and nobody discovers it until production. It also means neither model can evolve independently, so every schema change becomes a cross-team negotiation, defeating a big part of the reason for having separate services in the first place.
Like 'Chargebacks' meaning something specific to a bank's fraud team versus its accounting team — both departments use the word daily, both are right in their own building, and the bank doesn't force one merged definition; it has a designated liaison process that translates between the two whenever they need to exchange information.
saying these in an interview costs you the question
- Proposes making all teams agree on one universal 'Order' definition
- Doesn't mention bounded contexts or a context map when discussing the conflict
- Suggests just picking whichever team's definition is 'more correct'
- Has two contexts read/write the same table without any translation layer
- Treats the same word meaning different things as a bug to be eliminated rather than a normal, expected condition