Data Modeling: Embedded vs Referenced

Introduction

Relational databases normalize data into tables linked by foreign keys. MongoDB gives you a choice: embed related data inside one document, or reference other documents by _id. The wrong choice causes slow queries, 16 MB document limits, or painful updates. This chapter compares both approaches with blog and e-commerce examples and links to MySQL constraints for relational contrast.

Prerequisites

Query-Driven Design

Start from how the application reads and writes, not from an ER diagram alone.

Ask:

  1. What does the UI fetch in one request?
  2. How often does related data change independently?
  3. How large can the relationship grow (10 comments vs 100,000)?

Design for common access patterns; denormalize deliberately.

Embedding (Denormalization)

Store related records inside the parent document.

One read returns post + comments + author snapshot:

javascript
db.posts.findOne({ slug: "mongodb-modeling" })

When embedding fits

SignalExample
One-to-fewAddress on user, tags on post (small list)
Read togetherProduct with fixed specs
No independent lifecycleComments that die with the post
Updates are rareEmbedded author name rarely changes

Embedding drawbacks

  • Document size — unbounded comments array hits 16 MB
  • Stale copies — author name duplicated; update many posts if user renames
  • Write amplification — adding comment rewrites whole post document

Referencing (Normalization)

Store ObjectId (or business key) pointing to another collection.

Read post, then comments:

javascript
const post = db.posts.findOne({ slug: "referenced-post" })
db.comments.find({ postId: post._id }).sort({ createdAt: 1 })

Or join in aggregation — chapter 13.

When referencing fits

SignalExample
One-to-many / many-to-manyOrders, comments at scale
Independent updatesUser profile changes often
Large or growing childrenEvent log per user
Multiple parentsTag shared across posts

Referencing drawbacks

  • Multiple round trips in app code without $lookup
  • No referential integrity by default (unlike MySQL FOREIGN KEY)
  • Application must clean orphans or accept stale links

Hybrid: Summary Embed + Detail Reference

Homepage shows latest 3 comments embedded; full thread in comments collection:

javascript
db.posts.updateOne(
  { slug: "referenced-post" },
  {
    $set: {
      recentComments: [
        { author: "Bob", body: "Nice write-up.", createdAt: new Date() }
      ]
    }
  }
)

Full list: db.comments.find({ postId }). Sync recentComments on new comment (app logic or change stream).

MySQL vs MongoDB Mental Model

MySQLMongoDB
Tables + FOREIGN KEYCollections + embed or authorId
JOIN$lookup or second query
Normal form reduces duplicationEmbed for read speed; reference for scale
Schema in CREATE TABLEFlexible docs + optional validation — chapter 14

Anti-Patterns

Anti-patternProblemFix
Unbounded embed array16 MB, slow loadsSeparate collection
Deep nesting (5+ levels)Hard queriesFlatten or reference
Embed everything "because NoSQL"Update nightmaresReference mutable data
Reference one-to-few alwaysExtra queriesEmbed small stable data
No plan for duplicate fieldsStale author namesReference or accept + sync job

schemaVersion Field (Migration)

When document shape evolves:

javascript
db.posts.insertOne({
  schemaVersion: 2,
  title: "Versioned",
  slug: "versioned",
  metadata: { seoTitle: "Versioned Post" }
})

Application reads schemaVersion and migrates lazily on write or via batch job—no SQL-style ALTER TABLE, but still plan migrations.

Decision Checklist

  • Listed top 3 read patterns for the feature
  • Estimated max child count (10? 10,000?)
  • Decided embed vs reference per relationship
  • Planned index on reference fields (authorId, postId) — chapter 11
  • Documented stale-data rules for embedded copies

FAQ

Should I always embed for performance?

No—embed only when bounded and read together. Large or independent data belongs in its own collection.

Enforce references like foreign keys?

Use application validation + optional $jsonSchema validator. Transactions for multi-doc consistency — chapter 14.

Same as SQL denormalization?

Similar trade-off: duplicate data for read speed. MongoDB makes embedding first-class.

Graph relationships?

Many-to-many often needs a junction collection or array of IDs — Common Modeling Patterns.

Redis for nested data?

Redis stores serialized JSON strings—different layer. MongoDB persists documents; Redis caches them — Redis caching.