Document Schema Design
Purpose
Design MongoDB collections around how the data is read and written: embed what's accessed together, reference what's shared or unbounded, and keep every duplication's consistency story explicit. "Schemaless" means the database doesn't force design — not that design is optional.
When to Use
- After MongoDB is approved (
database-selection), before implementation.
- When extending collections for a feature.
- Not for relational schemas (
relational-schema-design) — and if this design keeps fighting for joins/transactions everywhere, escalate back to database-selection.
Inputs
- Domain model + the access patterns (which data is read/written together, how often, by what key).
- Cardinalities and growth expectations per relationship.
- Tenancy/ownership model (
../../backend/ownership-authorization).
Discovery Questions
- For each relationship: is the child data always read with the parent (embed signal) or accessed independently/shared across parents (reference signal)?
- What are the array growth bounds — can any embedded list grow unbounded (order items: bounded; log entries: unbounded)?
- Which fields genuinely vary across documents (the MongoDB justification), and which are actually uniform?
- What must be atomic — single-document updates (natural) or cross-document (needs
transactions and is a design smell in volume)?
Responsibilities
- Decide embed vs reference per relationship from access patterns:
- Embed: read/written as a unit with the parent, bounded size, not independently queried across parents.
- Reference: shared entities, unbounded/large collections, independently accessed or updated data.
- Design document shape per collection:
_id strategy, required core fields vs the genuinely variable part (name the variable part explicitly — a attributes/payload region, not chaos everywhere).
- Bound growth: no unbounded embedded arrays (16MB limit and rewrite cost are real) — bucket, paginate into child collections, or reference.
- Make duplication deliberate: denormalized snapshots (e.g. product name on an order) vs live references — each duplication has an update-propagation owner or an explicit "snapshot, never updated" ruling.
- Add schema validation (JSON Schema on collections and/or
mongoose-mongodb schemas): required fields, types, enums — variability is scoped, not total.
- Keep single-document atomicity as the default write model; flag cross-document invariants to
transactions.
- Tenancy: tenant key on every scoped document, in every query and index (
indexing, ownership-authorization).
- Document the unit as it is built —
docs/<app>/database/ (collection notes per area) (../../application-documentation).
Required Workflow
- List entities, relationships, cardinalities, and the concrete access patterns.
- Decide embed vs reference per relationship with the growth bound stated.
- Draft collection shapes: core fields, variable regions,
_id, tenant keys.
- Record every duplication with its consistency story.
- Define collection-level validation.
- Hand off to
mongoose-mongodb (or native driver plan) + indexing.
Decision Rules
- Access patterns decide, not object-model aesthetics: model the queries, not the class diagram.
- If most relationships end up referenced and queries keep joining (
$lookup everywhere), the domain is relational — say so (database-selection).
- Snapshot-vs-live is a business decision (does the old order show the old price?) — get it answered, don't guess.
- Unbounded growth → separate collection, no exceptions.
Rules
- Every collection has written validation for its stable core.
- Every duplication is recorded with propagation-or-snapshot ruling.
- Cross-document atomic needs are flagged, not silently assumed.
Anti-Patterns
- Designing documents as normalized tables (reference-everything) and re-implementing joins in app code.
- Unbounded embedded arrays (comments, events, logs inside a parent doc).
- "Flexible schema" as an excuse for undesigned, inconsistent field names/types across documents.
- Duplicated data with no owner — stale copies discovered by customers.
- Skipping validation because Mongo doesn't demand it.
Validation Checklist
Definition of Done
A recorded document design — per-relationship embed/reference decisions tied to access patterns, bounded shapes with validation, owned duplications, tenancy keys — ready for the data layer and indexing.
Related Skills
database-selection, mongoose-mongodb, indexing, transactions, concurrency, database-security, ../../backend/ownership-authorization, data-migration (reshaping later), ../../application-documentation.
Related Knowledge
../../../knowledge/ (access patterns, snapshot rulings).
Related References
../../../references/database/schema/ (collection sketches, when populated).
Context Loading Guidance
- Requires: domain model, access patterns with volumes, tenancy model.
- Does not require: Mongoose syntax, driver options, app code.
- May load:
mongoose-mongodb (expression), indexing (query keys).
- Stop when: collection designs + duplication ledger are recorded.
Token Efficiency Guidance
The relationship table (parent, child, access pattern, embed/reference, bound, duplication ruling) is the deliverable; example documents beat prose, one per collection.
1---2name: document-schema-design3description: Use to design MongoDB document schemas — embed vs reference decided per access pattern, document identity and shape per collection, growth-bounded arrays, duplication with consistency ownership, and schema validation despite "schemaless."4---56# Document Schema Design78## Purpose910Design MongoDB collections around **how the data is read and written**: embed what's accessed together, reference what's shared or unbounded, and keep every duplication's consistency story explicit. "Schemaless" means the database doesn't force design — not that design is optional.1112## When to Use1314- After MongoDB is approved (`database-selection`), before implementation.15- When extending collections for a feature.16- **Not** for relational schemas (`relational-schema-design`) — and if this design keeps fighting for joins/transactions everywhere, escalate back to `database-selection`.1718## Inputs1920- Domain model + the **access patterns** (which data is read/written together, how often, by what key).21- Cardinalities and growth expectations per relationship.22- Tenancy/ownership model (`../../backend/ownership-authorization`).2324## Discovery Questions2526- For each relationship: is the child data always read with the parent (embed signal) or accessed independently/shared across parents (reference signal)?27- What are the array growth bounds — can any embedded list grow unbounded (order items: bounded; log entries: unbounded)?28- Which fields genuinely vary across documents (the MongoDB justification), and which are actually uniform?29- What must be atomic — single-document updates (natural) or cross-document (needs `transactions` and is a design smell in volume)?3031## Responsibilities3233- Decide **embed vs reference per relationship** from access patterns:34 - **Embed**: read/written as a unit with the parent, bounded size, not independently queried across parents.35 - **Reference**: shared entities, unbounded/large collections, independently accessed or updated data.36- Design document shape per collection: `_id` strategy, required core fields vs the genuinely variable part (name the variable part explicitly — a `attributes`/`payload` region, not chaos everywhere).37- Bound growth: **no unbounded embedded arrays** (16MB limit and rewrite cost are real) — bucket, paginate into child collections, or reference.38- Make **duplication deliberate**: denormalized snapshots (e.g. product name on an order) vs live references — each duplication has an update-propagation owner or an explicit "snapshot, never updated" ruling.39- Add **schema validation** (JSON Schema on collections and/or `mongoose-mongodb` schemas): required fields, types, enums — variability is scoped, not total.40- Keep single-document atomicity as the default write model; flag cross-document invariants to `transactions`.41- Tenancy: tenant key on every scoped document, in every query and index (`indexing`, `ownership-authorization`).42- Document the unit as it is built — `docs/<app>/database/` (collection notes per area) (`../../application-documentation`).4344## Required Workflow45461. List entities, relationships, cardinalities, and the concrete access patterns.472. Decide embed vs reference per relationship with the growth bound stated.483. Draft collection shapes: core fields, variable regions, `_id`, tenant keys.494. Record every duplication with its consistency story.505. Define collection-level validation.516. Hand off to `mongoose-mongodb` (or native driver plan) + `indexing`.5253## Decision Rules5455- Access patterns decide, not object-model aesthetics: model the queries, not the class diagram.56- If most relationships end up referenced and queries keep joining (`$lookup` everywhere), the domain is relational — say so (`database-selection`).57- Snapshot-vs-live is a business decision (does the old order show the old price?) — get it answered, don't guess.58- Unbounded growth → separate collection, no exceptions.5960## Rules6162- Every collection has written validation for its stable core.63- Every duplication is recorded with propagation-or-snapshot ruling.64- Cross-document atomic needs are flagged, not silently assumed.6566## Anti-Patterns6768- Designing documents as normalized tables (reference-everything) and re-implementing joins in app code.69- Unbounded embedded arrays (comments, events, logs inside a parent doc).70- "Flexible schema" as an excuse for undesigned, inconsistent field names/types across documents.71- Duplicated data with no owner — stale copies discovered by customers.72- Skipping validation because Mongo doesn't demand it.7374## Validation Checklist7576- [ ] Access patterns documented per entity.77- [ ] Embed/reference decided per relationship with growth bounds.78- [ ] Collection shapes: core vs variable regions, `_id`, tenant keys.79- [ ] Duplications recorded with consistency ownership.80- [ ] Collection validation defined.81- [ ] Cross-document atomicity needs flagged to `transactions`.8283## Definition of Done8485A recorded document design — per-relationship embed/reference decisions tied to access patterns, bounded shapes with validation, owned duplications, tenancy keys — ready for the data layer and indexing.8687## Related Skills8889`database-selection`, `mongoose-mongodb`, `indexing`, `transactions`, `concurrency`, `database-security`, `../../backend/ownership-authorization`, `data-migration` (reshaping later), `../../application-documentation`.9091## Related Knowledge9293`../../../knowledge/` (access patterns, snapshot rulings).9495## Related References9697`../../../references/database/schema/` (collection sketches, when populated).9899## Context Loading Guidance100101- **Requires:** domain model, access patterns with volumes, tenancy model.102- **Does not require:** Mongoose syntax, driver options, app code.103- **May load:** `mongoose-mongodb` (expression), `indexing` (query keys).104- **Stop when:** collection designs + duplication ledger are recorded.105106## Token Efficiency Guidance107108The relationship table (parent, child, access pattern, embed/reference, bound, duplication ruling) is the deliverable; example documents beat prose, one per collection.