In MongoDB you don’t model your data — you model your queries. The right schema is the one that makes your app’s reads and writes fast and simple. Use this as a reference the next time you design a collection.
Model for your queries
- List your access patterns first. What does the app read and write most? Design around that, not around an ER diagram.
- Favor single-read documents. Data shown together should usually live together.
- Optimize for the common case, not the rare report — you can always aggregate for the exceptions.
Embed vs reference
- Embed when data is read together, is bounded, and belongs to one parent (one-to-one, one-to-few).
- Reference when data is large, unbounded, shared by many documents, or updated on its own.
- Watch document growth — embedded arrays that grow without limit risk the 16 MB cap.
- Duplicate deliberately. Copying a few fields to avoid a join is fine when reads dominate — just plan how to keep them in sync.
Relationships
- One-to-one — embed, unless the sub-document is large or accessed independently.
- One-to-few — embed as an array (e.g. a user’s addresses).
- One-to-many — reference from the many side (store the parent id on children).
- Many-to-many — reference with arrays of ids, or a linking collection when the link has its own data.
Indexing
- Index what you query and sort — every common filter and sort field.
- Order compound indexes by ESR — Equality, then Sort, then Range.
- Ensure sorts are index-backed to avoid in-memory sort limits.
- Drop unused indexes — they cost write throughput and RAM.
- Verify with
explain()that queries use the index you expect.
Useful patterns
- Computed pattern — store pre-computed totals/counts instead of recalculating on every read.
- Subset pattern — embed the few items you show often (e.g. latest 5 reviews); reference the rest.
- Extended reference — copy the couple of fields you always need from a related document to avoid a lookup.
- Bucket pattern — group time-series or high-volume events into bucket documents to cap array growth.
Anti-patterns to avoid
- Unbounded arrays that grow forever inside one document.
- Massive documents pushing the 16 MB limit.
- Over-normalizing into many collections that force multi-query joins on hot paths.
- Indexing everything — or nothing.
Quick checklist
- Access patterns written down before modelling
- Embed vs reference chosen per relationship
- No unbounded arrays / runaway document growth
- Indexes cover common filters and sorts (ESR)
- explain() confirms index usage on hot queries
FAQ
When should I embed vs reference in MongoDB?
Embed data that's read together and is bounded — one-to-one and one-to-few relationships. Reference data that's large, unbounded, shared across documents, or updated independently. Access patterns decide, not normalization rules.
How many indexes are too many?
Every index speeds reads but slows writes and uses memory. Index the fields you filter, sort, and join on — and no more. Watch for unused indexes and prefer compound indexes over many single-field ones.
What's the document size limit in MongoDB?
16 MB per document. Unbounded arrays are the usual way to hit it — if a field grows without limit, reference it in a separate collection or use the bucket pattern instead.

