DDD: bounded contexts
Code folders show a boundary but cannot say what a context deliberately leaves to others. This page answers where each context’s boundary is written, what that write-up must contain, and when a concept or a context is split in two.
Give every context a README in one template
Section titled “Give every context a README in one template”Impact: HIGH “Does not own” is the row no code can state
- Each bounded context gets a README in a fixed template, beside its glossary (see
ubiquitous language), an optional assumptions
file, and an
adr/folder whose decisions each end in “Revisit when”. A rootGLOSSARY-MAP.mdlists the contexts and their relationships. - “Owns” and “Does not own” are the boundary in prose. “Owns” names the feature folders; “Does not own” names the other context, which the code boundary cannot say.
- Accepted cost: docs to keep beside the code. A README whose “Owns” row drifts from the folders is a lie, and only review catches it.
❌ Incorrect — a free-form write-up that skips the boundary:
# LabelingLabeling handles labels on pull requests. See apps/server/src/labeling/.✅ Correct — the template, every row filled:
| Field | Current hypothesis || --- | --- || Problem | What recurring pain exists? || Frequent scenarios | What happens repeatedly? || First capability | The smallest coherent useful thing. || Owns | What truth or workflow belongs here (`apps/server/src/labeling/`). || Does not own | What deliberately stays elsewhere, and where. || Assumptions | The bets, by ID. || Entry criteria | What evidence says this context deserves implementation. || Non-goals | What is explicitly out. || Possible future pressure | Pressure that may reshape it. Not a promise. |Source: notes/04-architecture-and-api-design/ddd-strategic.md · Decision 1
Map every feature folder to exactly one context
Section titled “Map every feature folder to exactly one context”Impact: MEDIUM no folder without an owner, no context without folders
- A context maps to one or more feature folders. Every folder belongs to exactly one context.
- A context is domain or foundation, as in coupling and cohesion: foundation supplies mechanisms, domains supply meaning.
- A context per layer (
models/,rpc/,policy/) is not a context boundary at all.
❌ Incorrect — layers posing as contexts:
apps/server/src/models/ "Models context"apps/server/src/rpc/ "RPC context"apps/server/src/policy/ "Policy context"✅ Correct — feature folders, each owned by one context:
Labeling (domain) owns apps/server/src/labeling/Foundation (foundation) owns identity, orgs, authorization foldersSource: notes/04-architecture-and-api-design/ddd-strategic.md · Decision 1, Rejected
Meet the entry criteria before writing code
Section titled “Meet the entry criteria before writing code”Impact: MEDIUM a whole context is the costliest premature abstraction
- Entry criteria come before code. A context with no evidence behind its entry criteria stays a README.
- This is abstraction timing applied to a whole context.
❌ Incorrect — folders and services for a context nobody has asked for yet:
apps/server/src/billing/ created "because we'll need it"README.md Entry criteria: (empty)✅ Correct — the README holds the bet; the folder waits for the evidence:
README.md Entry criteria: a paying org asks for invoices Owns: (none yet)Source: notes/04-architecture-and-api-design/ddd-strategic.md · Decision 1
Split only on several strong signals from five tests
Section titled “Split only on several strong signals from five tests”Impact: HIGH prevents both a god-context and a context per noun
- Before splitting a concept (one type into two) or a context (one folder group into two), ask: vocabulary (would an expert use different words?), rules (materially different rules?), lifecycle (different states?), ownership (does a different capability decide validity?), change (do they evolve for different reasons?).
- One “yes” is not a split. Several strong signals together are. The PR that splits writes the answers in its description.
- For code folders, the data test in coupling and cohesion still applies on top: the five tests decide whether the meaning has split, that one whether the data has. Accepted cost: “strong signal” has no number; it takes judgement.
❌ Incorrect — one different rule, so a new context:
PR: split "Archived Labeling Rule" into its own contextWhy: archived rules can't be edited.✅ Correct — the five answers in the PR, and the split only when several are strong:
Vocabulary: same words → noRules: edit is blocked → yes (weak)Lifecycle: one more status → noOwnership: same capability → noChange: same reasons → noDecision: no split; add a status to Labeling RuleSource: notes/04-architecture-and-api-design/ddd-strategic.md · Decision 2