MongoDB Schema Design: Embedded vs Referenced Documents | Modeling Guide

MongoDB Schema Design: Embedded vs Referenced Documents | Modeling Guide

이 글의 핵심

Model MongoDB with measurable rules—co-location vs references, bucket patterns, indexes, and when multi-document transactions still cost more than a good schema.

Introduction

MongoDB is “schemaless,” but production services benefit from an explicit schema. Embedded vs referenced design is the core fork: store child data inside the parent document (embedded) or in another collection linked by id (referenced).

Embedded documents can end reads in one round trip; references control growth and reuse of documents. This article replaces gut feel with measurable criteria: size, frequency, consistency.

After reading this post

  • You can apply a checklist for embedded vs referenced designs
  • You get hints for 1:N, N:M, i18n/versioning variants
  • You understand the 16MB document limit and index interactions

Table of contents

  1. Concepts
  2. Hands-on implementation
  3. Advanced usage
  4. Performance comparison
  5. Real-world cases
  6. Troubleshooting
  7. Conclusion

Concepts

Embedded

Related data lives in one BSON document as nested arrays or sub-documents.

// User + addresses (embedded example)
{
  _id: ObjectId("..."),
  name: "Kim",
  addresses: [
    { label: "home", city: "Seoul", zip: "03000" },
    { label: "work", city: "Seongnam", zip: "13000" }
  ]
}

Pros: If you almost always read together, one query finishes the job. Updates in the same document can leverage atomic updates.

Referenced

Separate collections linked by ObjectId (or similar).

// users
{ _id: ObjectId("u1"), name: "Kim" }
// addresses
{ _id: ObjectId("a1"), userId: ObjectId("u1"), city: "Seoul" }

Pros: Child collections can grow large without bloating the parent. Fits models where many parents share the same child.

BSON document size

A single document is capped at 16MB. Data that could grow without bound should start as separate collections + indexes.


Hands-on implementation

Decision checklist

QuestionFavors embeddedFavors referenced
Read together?Almost alwaysRarely or partial reads
Updated together?Often at onceFrequently independent
Bounded child count?Small (e.g. a few addresses)Thousands/unbounded
Shared child across parents?RarelyOften
Strong cross-doc integrity?If it fits one documentMultiple collections + app logic/transactions

Pattern 1: 1:few — embedded

If comments are always shown with a post and counts are limited (or page-sized), consider embedding.

Pattern 2: 1:many (large) — referenced + indexes

// posts collection
db.posts.createIndex({ slug: 1 }, { unique: true });

// comments collection
db.comments.createIndex({ postId: 1, createdAt: -1 });
db.comments.find({ postId: postObjectId }).sort({ createdAt: -1 }).limit(50);

Pattern 3: Middle ground — buckets

For time-series logs, group readings per day to reduce document count.

{
  sensorId: "s1",
  day: ISODate("2026-03-30T00:00:00Z"),
  readings: [
    { t: ISODate("2026-03-30T00:05:00Z"), v: 23.1 },
    // ... batched for the day (design an upper bound)
  ]
}

Embedded vs referenced often lands on embedded-but-bucketed compromises.

$lookup (join)

For occasional joins on referenced models, aggregation $lookup works—but for hot paths, prefer two reads in application code or caching.


Advanced usage

  • i18n fields: { title: { ko: "...", en: "..." } } vs locale-split documents—match your translation workflow.
  • Partial updates: Use array filters and positional operators to reduce write conflicts on embedded arrays.
  • Schema validation: Enforce required fields/types with validator and JSON Schema for operational safety.

Performance comparison

AspectEmbeddedReferenced
Read latency (common path)Single findfind + find or $lookup
Write contentionHotspot if many writers to one docCan spread writes
IndexesNested field indexes—more complexPer-collection, often simpler
ConsistencySingle-doc atomic updatesApplication logic + transactions

Real-world cases

  • E-commerce orders: Header + dozens of line items often embedded; product master referenced.
  • Social feeds: Post referenced; like counts denormalized with eventual consistency via events.
  • B2B multi-tenant: Include tenantId in every query with a leading index.

Troubleshooting

SymptomCause / fix
Document near 16MBSplit arrays, archive collections, buckets
Slow update on large embedded arraysSplit documents or move to references
Broken referential integrityApp validation + transactions if needed; explicit delete policies (soft delete)
Index used but still slowWorking set size, projection minimization, covering indexes

Conclusion

MongoDB embedded vs referenced design is not “anything goes because NoSQL”—it is locking read patterns and growth boundaries into code. Compare with relational transaction/join trade-offs using the PostgreSQL vs MySQL guide, apply this checklist first, then validate with load tests.