Bounded contexts (DDD-lite)
Give each context one meaning per term and its own model; map the links, and translate where an outside model enters.
Predict first
Before you read: predict
ExamHall's Proctoring and Billing teams both say "user". To Proctoring, a user is a person sitting an exam, with an identity check, a seat and an attempt in progress. To Billing, a user is whoever is invoiced: usually a test centre that pays for many candidates. Today both teams read and write one users table through one User class, and a change by either team waits for the other's review.
What should the next version do?
The 30-second version
A bounded context is a part of a system where each term has one meaning and one model. Different contexts may model one thing differently and share its id. A context map names every context and shows each link: who is upstream, what is shared, where it is translated. An anti-corruption layer translates an upstream model into yours.
Give each context one meaning per term and its own model; map the links, and translate where an outside model enters.
How it works
Step 1 of 14
In the kitchen
- "the loss" is the event to the call centre, the covered damage less the excess to the adjuster, the money reserved and paid to finance → one term, a meaning per context (steps 1–2)
Analogy: A home insurer's claims office and one burst-pipe claim
One claim number follows the claim through the call centre, the loss adjuster, finance and the supplier desk.
| At the claims office | In bounded contexts |
|---|---|
| "the loss" is the event to the call centre, the covered damage less the excess to the adjuster, the money reserved and paid to finance | one term, a meaning per context (steps 1–2) |
| each desk keeps its own form; nobody fills one giant form for all four | a model per context, none for everything (steps 2–3) |
| every form carries the claim number | contexts share the id (step 4) |
| the supplier's quotes come in its catalogue codes; the supplier desk turns them into the insurer's item categories and asks the supplier to change nothing | an anti-corruption layer (step 12) |
| when the supplier renumbers its catalogue, only the supplier desk's code list changes | the change lands in the layer (step 13) |
| the wall chart of desks, what passes between them and where it is translated, as the office works this year | the context map (steps 8–9) |
In real terms: each context's code keeps its own model of a shared thing, joined to the other contexts by its id, and an outside model is translated in one place.
Breaks down when: a desk can phone the supplier to ask what a code means. A translation layer has nobody to ask, so a code it does not map must be refused or flagged in code.
Contrasting case: a one-person plumbing firm takes the call, fixes the pipe and sends the bill. One person speaks one language, so one context is right.
Examples, run for real
4.1 ExamHall EXH-2360: one Attempt class for three teams
EXH-2360: Proctoring, Grading and Billing shared one Attempt class. Billing asked to charge sittings that a proctor voided after more than 20 minutes, and a developer widened the class's isComplete(), which Grading also calls. The script runs five attempts; a-4 was voided at minute 25:
4.1 shared Attempt, before Billing's change: graded a-1, a-2, a-3 · charged a-1, a-2, a-3
4.1 shared Attempt, after Billing's change to isComplete(): graded a-1, a-2, a-3, a-4 · charged a-1, a-2, a-3, a-4
Grading graded a voided attempt (step 3). MR !2660 gives each team its own model, built from Proctoring's session record and joined by attempt_id, and moves Billing's rule into Billing's model:
4.1 MR !2660 a-4 as proctoring.Session: attemptId, candidateId, centreId, state, minutes, deadline
4.1 MR !2660 a-4 as grading.Submission: attemptId, answers, final
4.1 MR !2660 a-4 as billing.Charge: attemptId, centreId, minutes, chargeable
4.1 MR !2660 after Billing's change to Charge.chargeable: graded a-1, a-2, a-3 · charged a-1, a-2, a-3, a-4
One attempt, three shapes and one shared id (step 4). Billing's change now stays in Billing.
4.2 Proof domain, media streaming: three counts of "subscribers"
One week of a streaming service's accounts, counted by three teams, each with its own meaning of "subscriber":
4.2 "subscribers" this week: Billing 2 (paying accounts) · Playback 5 (profiles that streamed) · Marketing 3 (newsletter emails)
Each count answers its own team's question correctly. One word with three meanings asks for three models (steps 1, 5).
Try it
Answer first, then check. Nothing is saved.
Question 1 of 5
A streaming service's Recommendations team reads titles from the Catalogue team's code:
Recommendations' six other files import CatalogueTitle in the same way, and Recommendations defines no title type of its own. Catalogue's team plans its releases without Recommendations.
Which relationship does the context map show from Recommendations to Catalogue?
// recommendations/rank.jsimport { CatalogueTitle } from '../catalogue/model.js';/** @param {CatalogueTitle[]} titles */export const rank = (titles) => titles.filter((t) => t.kind === 'EP' && t.rights_flag !== 'X').sort(byViews); // recommendations/rows.jsimport { CatalogueTitle } from '../catalogue/model.js';/** @param {CatalogueTitle} t */export const rowLabel = (t) => `${t.ttl} · S${t.season_no}`;Question 2 of 5
A ticket company's new head of engineering asks for its context map by Friday. Booking, Seating, Checkout and Venue Data are four teams with their own models. Seating and Checkout live in one codebase and deploy together. Booking reads Venue Data's seat plans through a translator of its own. Leadership plans to merge Booking and Seating next year, and this quarter's incidents all began in Booking.
Which drawing should she get?
Question 3 of 5
A payments company's Refunds team needs each card payment's state from its card gateway, a third-party system bought years ago. The gateway keeps authorisations, captures and chargebacks as one record type, told apart by two-letter codes whose meaning differs by card scheme. Its vendor ships changes to all its customers on its own schedule. Refunds' own model has Payment, Capture and Refund.
How should Refunds connect to the gateway?
Question 4 of 5
A chat company's Messaging team owns one codebase with one model. At a planning meeting, three of its people describe their work:
- Backend engineer: "A conversation is a thread of messages between its members. Some of our files call it a thread and others call it a conversation.
- Moderation lead: "When a message is reported, we open a conversation: what was reported, who reported it, and our decision.
- Push engineer: "Each new message is stored once and reaches every member's devices within a second.
Which word shows a second model forming inside Messaging's one model?
Question 5 of 5
Proctoring sends a record for each finished attempt:
Billing conforms to Proctoring's model in three files: invoice.js charges each centre for its sittings over 20 minutes, refunds.js refunds voided sittings of 20 minutes or less, and centre-report.js counts each centre's sittings. Grading reads Proctoring through an anti-corruption layer, from-proctoring.js; its five other files mark, moderate, appeal, publish and export the answers. Next term Proctoring renames every field except attempt_id. Each file is fixed by changing the field names in its own code.
How many of Billing's and Grading's files need that fix before all their checks pass again?
{ attempt_id: 'a-31', cand: 'c-8', centre: 'TC-9', state: 'VOID', mins: 25, answers: [{ q: 1, v: 'B' }] }Go deeper
Pro tips
- When two dashboards disagree about a count with the same name ("subscribers", "active users"), compare each team's definition of the word before you debug the numbers.
When: two reports built from one dataset give two counts for one name (steps 1, 5). Verify:
4.2 "subscribers" this week: Billing 2 (paying accounts) · Playback 5 (profiles that streamed) · Marketing 3 (newsletter emails)
Source
Both are stable texts (dated 2014 and 2015) and make no vendor claim. Valid as of 2026-10-01.
- Martin Fowler, Bounded Context (bliki, 15 January 2014), the curriculum row's first source: https://martinfowler.com/bliki/BoundedContext.html (
fowler-bounded-context.txt). - Eric Evans, Domain-Driven Design Reference: Definitions and Pattern Summaries (Domain Language, 2015, CC BY 4.0): https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf (
ddd-reference.txt). The row's second source is Evans's book Domain-Driven Design (2003), which cannot be fetched as text; this booklet holds Evans's own summaries of the book's patterns, which he writes he "extracted" from it.
Related lessons
Checked against the tools and docs as of 2026-10-01.