Lesson 0001 · The document model

The document model

The document is the unit of three things at once — storage, atomicity, and schema design. Get that one fact and embedding, "data accessed together stored together," and "when do I even need a transaction?" all derive themselves.

~9 minRetrieval quiz + relational contrastCheat sheet: here

You already know the relational model cold: you normalize — split an entity across tables so each fact lives once — and then join at read time to put it back together. A clustered index (InnoDB) or a heap plus secondary indexes (Postgres) stores the rows; foreign keys wire the tables together. MongoDB makes the opposite first move, and this lesson is the keystone the whole course hangs off. Get it and a surprising amount derives itself: why you embed instead of join, why one write can be atomic without a transaction while another can't, and why "design for your access pattern" is the whole game.

The one idea

The basic unit of data in MongoDB is a document: an ordered set of field-and-value pairs, stored as BSON — a binary superset of JSON with more types (dates, 64-bit ints, binary, ObjectId). MongoDB Manual: "MongoDB stores data records as BSON documents. BSON is a binary representation of JSON documents, though it contains more data types than JSON." A value can itself be a document, an array, or an array of documents — so a document is a tree, not a flat row. Documents live in collections (loosely, the table); a collection needs no fixed schema.

Every document has an _id field that acts as its primary key and is unique within the collection; if you don't supply one, the driver generates an ObjectId. MongoDB Manual: "each document stored in a collection requires a unique _id field that acts as a primary key … If … omitted … the driver automatically generates an ObjectId for the _id field." And a single document is capped: 16 MB — a deliberate limit so one document can't hog RAM or bandwidth. MongoDB Manual: "The maximum BSON document size is 16 megabytes … helps ensure that a single document cannot use an excessive amount of RAM or … bandwidth."

THE SAME DATA, TWO MODELS Relational (normalize + join) MongoDB (embed in one document) ┌────────────┐ ┌───────────────┐ { │ authors │ │ posts │ _id: ObjectId("..."), │ id name │◀──│ id author_id │ title: "Hello", └────────────┘ │ title │ author: { name: "Ann" }, ← embedded ▲ └───────────────┘ comments: [ ← array of docs │ ┌───────────────┐ { user: "Bo", text: "hi" }, └───────────│ comments │ { user: "Cy", text: "yo" } │ id post_id … │ ] └───────────────┘ } Read a post = JOIN 3 tables. Read a post = fetch ONE document.
The key insight

The core principle of MongoDB data modeling is that data that is accessed together should be stored together. MongoDB Manual: "A key principle of data modeling in MongoDB is that data that is accessed together should be stored together." So the default move is embedding: nest the related data inside the one document you'll read. That turns a multi-table join into a single-document lookup and avoids complex joins … while improving performance. MongoDB Manual: "Embedding data … lets you avoid application joins, which reduces queries … and improves read performance."

The second consequence: atomicity comes from the document boundary

Here is where the document being the unit of storage pays off as the unit of atomicity. A write to a single document is atomic — all-or-nothing — even when it touches many fields or nested arrays. MongoDB Manual: "In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple embedded documents within a single document." So if you embedded the order and its line-items in one document, updating the order and its items together is atomic for free — no transaction needed.

The moment your write must change more than one document atomically, the free ride ends: each document's change is still atomic, but the operation as a whole is not, and other operations can interleave. For true multi-document atomicity you reach for a distributed transaction. MongoDB Manual: "When a single write operation … modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic. … For situations that require atomicity of reads and writes to multiple documents … MongoDB supports distributed transactions." And MongoDB's own guidance is telling: a transaction costs more than a single-document write, and its availability should not be a replacement for effective schema design — often, embedding removes the need entirely. MongoDB Manual: "In most cases, multi-document transactions incur a greater performance cost … and the availability of transactions should not be a replacement for effective schema design."

The relational mirror

In Postgres/InnoDB, atomicity is a property of the transaction — you wrap N statements across M tables in BEGIN … COMMIT and the engine's MVCC + undo/redo make them all-or-nothing. In MongoDB, atomicity is a property of the document first: shape your data so the thing that changes together lives in one document, and you get atomicity without paying for a transaction. Same goal — consistency — reached from opposite starting points: the transaction vs the document boundary.

So when don't you embed?

Embedding is the default, not a law. You reference instead (store an _id and look it up in another collection — the closest thing to a foreign key) when the related data is large, grows without bound, or is accessed on its own. The manual's example: an e-commerce product embeds its five most-recent reviews for the product page, but keeps the full review history in a separate collection because older reviews aren't accessed as frequently. MongoDB Manual: "store … reviews … in a separate collection if … not accessed as frequently." Two forces pull against embedding: the 16 MB ceiling (an unbounded array will eventually blow it) and duplication (embedded copies must be updated in every place they were copied). We give schema design its own lesson (0006) — for now, hold the shape: embed what you read together; reference what's big or independent.

MongoDB vs the relational model — the keystone contrast

QuestionPostgres / InnoDB (you know this)MongoDB (this course)
Unit of data?A row in a fixed-schema tableA document (BSON tree) in a collection
Schema?Declared up front, enforcedFlexible — shape per document
Relate data by…Normalize + join at readEmbed (or reference) — store together
Read one entity =JOIN across tablesFetch one document
Atomicity is a property of…The transactionThe document (single-doc = atomic)
Multi-entity atomic write?Always a transactionTransaction — or embed so you don't need one

Check yourself

Answer from memory — the effortful recall is what builds retention. Feedback is immediate.

What is the fundamental unit of data in MongoDB?

The unit is a document — field/value pairs stored as BSON (binary JSON with extra types), living in a collection. It's a tree, not a flat row, and it can hold nested documents and arrays.

A write that modifies many fields and nested arrays of one document is:

Single-document writes are atomic even when they touch multiple embedded documents or arrays. Atomicity is a property of the document boundary — that's the payoff of embedding.

The core principle that decides how to model data in MongoDB is:

"Data that is accessed together should be stored together." You model to your access pattern, which makes embedding the default — the opposite of the relational normalize-then-join instinct.

You must atomically update three separate documents. What does MongoDB require?

Each document's change is atomic, but the multi-document operation as a whole is not. True cross-document atomicity needs a transaction — though good schema design (embedding) often removes the need.

Which pressure most directly pushes you to reference instead of embed?

A single document maxes out at 16 MB, so an ever-growing embedded array will eventually break. Large, unbounded, or independently-accessed data is the signal to reference (store an _id) instead.
Optional — when you have an instance

No Mongo handy yet, so this is a lab to run later (Docker: docker run --rm -p 27017:27017 mongo, then mongosh). It makes the whole lesson visible in a few commands:

db.posts.insertOne({ title: "Hello",
  author: { name: "Ann" },
  comments: [ { user: "Bo", text: "hi" } ] })   // one embedded document
db.posts.findOne()                              // _id was auto-generated as an ObjectId
db.posts.updateOne({ title: "Hello" },
  { $push: { comments: { user: "Cy", text: "yo" } },
    $set:  { "author.name": "Ann B." } })       // touches array + nested field — ATOMIC, no txn
db.posts.findOne()                              // both changes applied together

That one updateOne changing a nested field and an array together, atomically, with no transaction — that is the keystone made concrete. Bring the output to your teacher and we'll read it together.

Primary source — read this next

MongoDB Manual — Documents (short; the BSON, _id, and 16 MB facts), then Data Modeling for the "accessed together, stored together" principle and embedding vs referencing. For the atomicity boundary, Atomicity and Transactions is two screens and worth reading in full.