Back to the map

Module 07 · Databases · Lesson 7 of 13

Document databases

Self-contained JSON documents instead of rows across tables: when embedding beats joining, how to model one-to-many and many-to-many, the schema flexibility trap, and what MongoDB-style stores give up and get back.

IntroShould-knowJunior 9 min Updated Nov 2026

Why it matters

An order with its items, shipping address and payment summary is one thing to the application and five tables to a relational database. Document stores let you store it as one thing. That matches how code thinks and makes the common read a single fetch, which is why document databases are popular for product catalogues, user profiles, content and anything else that is naturally a self-contained record. The cost arrives when the data turns out to be more related than it looked.

The idea

A document database stores records as nested documents, usually JSON or a binary variant, grouped into collections. Each document can have its own shape; fields can be nested objects and arrays; indexes can be built on any path, including inside arrays. Queries filter by fields and return whole documents or projections, and aggregation pipelines do grouping and transformation on the server.

Embedding versus referencing

 Embed (nest inside the document)Reference (store an id, fetch separately)
WhenThe child belongs to exactly one parent and is read with it (order items, address)The child is shared or large or updated independently (the customer, a product catalogue entry)
ReadOne fetch returns everythingTwo fetches or an application-side join
WriteUpdating the parent rewrites the document; atomicUpdates in one place; no duplication
GrowthUnbounded arrays (comments on a viral post) blow past document size limitsScales to any count
ConsistencySnapshot semantics: embedded copies can go staleAlways current

The guiding rule: embed what you read together and own completely; reference what is shared, large or unbounded. Most schemas mix both, and the decision is per relationship, not per collection.

order 9001status, total, placed_atitems: [ {sku, qty, price}, … ]customer_id: 'c_42'referencecustomer c_42own document
An order as one document: items embedded because they belong to it and are read with it; the customer referenced because it is shared and changes independently.

Schema flexibility, honestly

"Schemaless" means the database does not enforce a shape; the application still depends on one. Flexibility is valuable when records legitimately differ (products with category-specific attributes) or when a prototype is changing daily. It becomes a liability when five generations of a record shape coexist and every reader must handle all of them. Mature teams add schema validation at the collection level and run migrations anyway, either eagerly or lazily on read. The flexibility is a tool to use deliberately, not a reason to skip design.

Transactions, indexes and queries

Single-document writes are atomic, which, combined with embedding, covers many cases that would need a multi-row transaction in a relational store. Multi-document transactions exist in MongoDB and DynamoDB but cost more and are not the idiom. Indexes work like relational indexes, including compound and multi-key (into arrays) indexes, and the same rules apply: unindexed filters scan the collection. Aggregation pipelines handle grouping and joins ($lookup) on the server, slower than relational joins and best kept for reporting rather than hot paths.

Where it fits

Catalogues, profiles, content management, event payloads, settings, and any entity that is read whole and owned whole. It fits poorly where entities are heavily shared and many-to-many (social graphs, financial ledgers), where analysts need ad-hoc cross-entity queries, or where strict multi-entity invariants matter. Note that Postgres jsonb offers indexed documents inside a relational database, which is often the pragmatic middle for a mostly-relational system with a few document-shaped tables.

Trade-offs

Document stores buy a natural fit with application objects, single-fetch reads, horizontal scaling and per-record flexibility, and pay with duplicated data that can go stale, weaker cross-document guarantees, and the hidden schema that lives in the application. Done with discipline (deliberate embedding, validation, migrations) the model is excellent for the shapes it suits.

In an interview

Justify by read shape: "The product catalogue is a document store because each product is read whole, has category-specific attributes, and is never joined on the hot path. Items embed in the order because they belong to it; the customer is referenced because it is shared. Collection validation enforces the shape we rely on." Then show you know the limit: "Payments stay relational because they need multi-entity transactions and ad-hoc reporting."

Check yourself

A blog stores each post as a document with all its comments embedded. A post goes viral and gets 200,000 comments. What goes wrong, and what was the better model?

Document databases · Backplane