[2026] MongoDB Schema Design: Embedded vs Referenced Documents | Modeling Complete Guide

[2026] MongoDB Schema Design: Embedded vs Referenced Documents | Modeling Complete Guide

이 글의 핵심

Choose embedded vs referenced collections using document size, read/write patterns, 1:N growth, and consistency—plus buckets, partial updates, and the 16MB limit.

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. 아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 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). 다음은 간단한 javascript 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 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

아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 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. 아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

{
  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.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3