mongoui
Schema·July 1, 2026·7 min read

MongoDB schema design for e-commerce, with a worked example

An e-commerce schema in MongoDB comes down to a few decisions: embed the cart, snapshot the order, reference the reviews. Here is a concrete products-cart-orders model and the one denormalization that saves you later.

Arctic Glass illustration for MongoDB schema design for e-commerce, with a worked example

An e-commerce schema in MongoDB is a series of small modeling calls, and most of them answer to one question: is this data read with its parent, and is it bounded? Embed when yes, reference when no. There is one rule specific to commerce that trips people up, and we will get to it, because it is the one that turns into a bug six months in. Here is a concrete model you can adapt.

Products: embed the variants

A product is read as a unit, and its variants (sizes, colors) are bounded, so embed them:

// products
{
  _id: ObjectId("..."),
  slug: "arctic-hoodie",
  name: "Arctic Hoodie",
  price: 6900,                 // cents
  variants: [
    { sku: "AH-S-BLU", size: "S", color: "blue", stock: 12 },
    { sku: "AH-M-BLU", size: "M", color: "blue", stock: 4 }
  ],
  ratingAvg: 4.6,             // summary of reviews, kept here for the listing
  ratingCount: 218
}

One read renders the product page. The ratingAvg and ratingCount are a deliberate copy of a summary you always show, so the listing does not have to touch the reviews collection.

Cart: embed the items, look up the price live

A cart is small, bounded, and always read with its owner. Embed it, store the productId and quantity, and resolve the current price when you display it:

// users (or a carts collection keyed by user)
{
  _id: 123,
  cart: [
    { productId: ObjectId("..."), sku: "AH-M-BLU", qty: 1 },
    { productId: ObjectId("..."), sku: "MUG-01",   qty: 2 }
  ]
}

The cart does not store price, because a cart should reflect the price now. You look that up from the product at display time. The moment money changes hands, that changes.

Orders: snapshot everything (this is the one)

Here is the rule that is specific to commerce. An order is a historical record, and prices and products change. If your order references the live product, then a price change next month silently rewrites what the customer paid, and a deleted product breaks the order entirely. That is the classic version of a schema decision coming back to bite: the reference was correct for the cart and wrong for the order.

So at checkout, copy the details into the order:

// orders
{
  _id: ObjectId("..."),
  userId: 123,
  placedAt: ISODate("2026-07-01T14:12:00Z"),
  status: "shipped",
  items: [
    { productId: ObjectId("..."), name: "Arctic Hoodie",
      sku: "AH-M-BLU", price: 6900, qty: 1 }   // price AS OF purchase
  ],
  total: 6900
}

The name and price are frozen at purchase time. The product can change or vanish and the order still tells the truth. This is intentional denormalization: the cart references, the order snapshots, and they are different documents for a reason. The general principle behind that call is in how to design a MongoDB schema.

Reviews: reference, do not embed

Reviews are unbounded. A popular product can gather thousands, and a document caps at 16MB, so embedding them is a wall you will hit. Give them their own collection:

// reviews
{ _id: ObjectId("..."), productId: ObjectId("..."), userId: 123, stars: 5, body: "..." }

Keep the summary (ratingAvg, ratingCount) on the product for the listing, and recompute it when a review lands. Embed the summary you always show, reference the full set you sometimes page through.

Then watch it drift

A good e-commerce schema still drifts under real traffic: an import writes price as a string, a new variant field appears on some products and not others, an order slips through without a total. Those are the mismatches that break a checkout query without throwing an error. Catching them is analyzing your schema, and enforcing the shape once it settles is $jsonSchema validation.

mongoui's Schema doctor samples each collection and reports the real field shapes and type drift, so you can confirm the products, carts, and orders still match the model you designed. It runs on your machine, so your catalog and customer data never leave your network. Embed the cart, snapshot the order, reference the reviews, and check now and then that reality still agrees with the diagram.

Frequently asked questions

How should I design a MongoDB schema for e-commerce?

Model around access patterns. Embed data read with its parent and bounded in size (a product's variants, a cart's items), reference data that is large or shared (reviews, a customer's full order history). The one rule specific to commerce: an order must snapshot the price and product details at purchase time, not reference the live product.

Should a shopping cart be embedded or a separate collection in MongoDB?

Embed the cart items on the user or cart document. A cart is small, bounded, and always read with its owner, so one read gets the whole cart. Store the productId and quantity, and look up the live price at display time so the cart reflects current pricing.

Why should a MongoDB order snapshot the product price?

Because prices and product details change, and an order is a historical record. If the order references the live product, a later price change or a deleted product rewrites or breaks the order. Copy the name, price, and options into the order's line items at checkout so the record stays true.

Should product reviews be embedded in the product document?

No. Reviews are unbounded, a popular product can have thousands, and a document is capped at 16MB. Store reviews in their own collection with a productId reference. Embed only the summary you always show, like an average rating and count, on the product.