Home / Databases / MongoDB

DatabasesHow-toIntermediate

Advisory: MongoDB schema design - embed or reference, and the patterns that keep documents fast

MongoDB schemas should follow how the application reads and writes data, not a normalised relational model. Embed data read together, reference data that grows without bound or is shared, use patterns such as subset, bucket and computed fields, and enforce structure with schema validation.

Deepak Bhardwaj3 min read

Applies toMongoDB 6.0 and later (self-managed and Atlas); Azure Cosmos DB for MongoDB has similar modelling considerations with its own limits

Start from access patterns

List the main operations with their frequency:

Operation Frequency Data needed
Show order with lines Very high Order + lines + customer name
Add order line High Order
Customer order history Medium Orders by customer, recent first
Update customer address Low Customer

Design documents so the most frequent operations touch one document.

Embed or reference

Embed when Reference when
Data is read together Data is read separately
"Contains" relationship (order → lines) Many-to-many or shared entities (products, customers)
Child count is bounded and small Child list grows without bound (comments, events)
Updates are mostly to the whole aggregate Child data updated independently and often

Document size limit is 16 MB, but performance suffers long before that with large, frequently rewritten documents.

Useful patterns

Pattern Use
Extended reference Copy a few fields (customer name) into the order to avoid joins; accept occasional updates
Subset Keep the latest N items embedded (last 10 reviews), the rest in another collection
Bucket Group time-series readings per device per hour into one document (or use time series collections)
Computed Store totals/counters updated on write instead of aggregating on every read
Outlier Handle the rare huge case (a celebrity with millions of followers) separately
Schema versioning schemaVersion field to migrate documents gradually
// Order with embedded lines and an extended reference to the customer
{
  _id: ObjectId("..."),
  customer: { id: 1042, name: "A. Kumar" },
  orderDate: ISODate("2026-10-25"),
  lines: [ { sku: "P-100", qty: 2, price: 499 }, { sku: "P-220", qty: 1, price: 1299 } ],
  total: 2297,
  schemaVersion: 2
}

Validate structure

db.runCommand({
  collMod: "orders",
  validator: { $jsonSchema: {
    bsonType: "object",
    required: ["customer", "orderDate", "lines"],
    properties: { lines: { bsonType: "array", maxItems: 500 } }
  }},
  validationLevel: "moderate"
})

Review checklist for a design

  • Do the top operations need only one document (or one targeted query)?
  • Can any array grow without limit?
  • Are duplicated fields acceptable to update on change?
  • Do indexes support the queries (see the WiredTiger/index post)?
  • If sharding later, does the model work with a sensible shard key?

Tip

Prototype with realistic data volumes and run the top queries with explain(). Schema problems show up quickly in documents examined and document sizes.

Checklist

  • Access patterns listed with frequency
  • Embed/reference decided per relationship
  • Unbounded arrays avoided
  • Patterns applied where they fit
  • Schema validation on key collections
  • Prototype tested with explain()