Skip to content

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 root GLOSSARY-MAP.md lists 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:

# Labeling
Labeling 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 folders

Source: 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 context
Why: archived rules can't be edited.

✅ Correct — the five answers in the PR, and the split only when several are strong:

Vocabulary: same words → no
Rules: edit is blocked → yes (weak)
Lifecycle: one more status → no
Ownership: same capability → no
Change: same reasons → no
Decision: no split; add a status to Labeling Rule

Source: notes/04-architecture-and-api-design/ddd-strategic.md · Decision 2