mongoui
Schema·June 27, 2026·7 min read

MongoDB schema design patterns worth knowing (with examples)

Beyond embed-or-reference, a handful of named MongoDB schema patterns solve recurring problems: too many similar fields, unbounded growth, expensive recomputation. Here are the ones you will actually reach for.

Arctic Glass illustration for MongoDB schema design patterns worth knowing (with examples)

Once you have the big decision down (embed what you read together, reference what is large or shared), MongoDB schema design becomes a set of named patterns for recurring problems. You do not have to invent these; the MongoDB community named them years ago. Here are the four you will actually reach for, each with the problem it solves.

The attribute pattern: many similar fields

The problem: a product catalog where every item has a different set of specs. A laptop has RAM and screen size; a shirt has size and color; a book has an ISBN. Model each as its own field and you get wide, sparse documents and a new index for every attribute anyone might filter on.

The pattern: store the variable fields as an array of key-value pairs, and index the array once:

{
  name: "Arctic Laptop",
  specs: [
    { k: "ram_gb", v: 32 },
    { k: "screen_in", v: 14 },
    { k: "weight_kg", v: 1.2 }
  ]
}

db.products.createIndex({ "specs.k": 1, "specs.v": 1 })

One index now serves a filter on any attribute. You traded a pile of single-purpose indexes for one, which is a strict win on write cost. The MongoDB pattern catalog has the full set if you want to go deeper.

The bucket pattern: high-volume, append-only data

The problem: a sensor writes a reading a second. One document per reading is millions of tiny documents and an index the size of a small country. And if you embed the readings in the device document, the array grows without bound toward the 16MB wall.

The pattern: bucket many readings into one document, usually by time window:

{
  deviceId: "sensor-7",
  hour: ISODate("2026-06-27T14:00:00Z"),
  readings: [
    { t: 0, temp: 21.4 },
    { t: 1, temp: 21.5 }
    // up to one hour of readings, then a new bucket
  ],
  count: 3600
}

One document per device per hour instead of one per second. Far fewer documents, a far smaller index, and the array has a natural ceiling so it never becomes an unbounded-growth problem. This is how time-series data wants to be stored.

The computed pattern: precompute the expensive answer

The problem: every product page shows an average rating, and computing it means aggregating the reviews collection on every page load. Cheap once, expensive at scale.

The pattern: compute it when the data changes, store the result, and read it for free:

// on the product, kept current when a review lands
{ name: "Arctic Hoodie", ratingAvg: 4.6, ratingCount: 218 }

You do the math on write, which is rare, instead of on read, which is constant. The e-commerce schema post uses exactly this for product ratings.

The extended reference: copy a few fields to skip a join

The problem: an order references a customer by id, but every time you display the order you also want the customer's name and city, so you do a lookup.

The pattern: copy the handful of fields you always show onto the order, and keep the reference for everything else:

{
  _id: ObjectId("..."),
  customer: { id: 123, name: "Sam Rivers", city: "Portland" },  // extended reference
  total: 6900
}

You denormalize a little, on purpose, to skip a join on the common read. The trade is keeping the copied fields in sync when they change, so use it for fields that rarely change (a name, a city) and not for volatile ones. The order's price snapshot in the e-commerce example is a close cousin of this.

Patterns drift too

Every one of these patterns denormalizes something, which means two copies that can disagree: a stale ratingAvg, an extended reference whose name went out of date, a bucket that stopped rolling over. That is schema drift wearing a design pattern, and it is the price of the read performance you bought. The foundations are in MongoDB schema design; these patterns sit on top.

mongoui's Schema doctor samples the collection and reports the real shapes, so a computed field that quietly stopped updating, or a bucket document that grew past its intended size, shows up as the anomaly it is. It runs on your machine, so your documents never leave your network. Reach for the pattern that fits the problem, and check now and then that the denormalized copy still agrees with the source.

Frequently asked questions

What are MongoDB schema design patterns?

Named, reusable solutions to recurring modeling problems in MongoDB. The common ones are the attribute pattern (for many similar fields), the bucket pattern (for time-series and high-volume data), the computed pattern (precompute expensive results), and the extended reference (copy a few fields to avoid a join).

What is the attribute pattern in MongoDB?

A pattern for documents with many similar fields, or fields that vary between documents. Instead of a wide, sparse document, you store the fields as an array of key-value pairs, then index that array once. It keeps the index count down and handles products or specs with dozens of optional attributes.

What is the bucket pattern in MongoDB?

A pattern for high-volume data like sensor readings or events. Instead of one document per reading, you group many readings into one bucket document (say, one per device per hour). Far fewer documents, smaller indexes, and it sidesteps unbounded array growth.

How do I model nested data in MongoDB?

Embed nested structures when they are bounded and read with the parent, and reference them when they are large or shared. For arrays that only grow, the bucket pattern or a separate collection prevents documents from swelling toward the 16MB limit.