MongoDB Module 5: Schema Design and Validation
Edited by Dev Kilaru, Senior Data Analyst. How we make our content
Introduction
MongoDB's main design rule is to model data the way your application uses it. Data that is read together should usually be stored together. This module teaches the embed versus reference decision, a few proven patterns, and how to add validation so the flexibility does not turn into chaos.
Prerequisites
- Module 4 completed
Step-by-Step Guide
Step 1: Embed or reference
Embed when data is read together, belongs to one parent, and the embedded part is small and bounded (an address, line items in an order). Reference when data is shared by many parents, changes independently, or can grow without limit (all orders of a customer, comments on a popular post). Embedding gives faster reads and atomic updates of one document; referencing avoids duplication and oversized documents.
Embed: order { items: [ {sku, qty, price} ] } read together, bounded
Reference: order { customerId: ObjectId(...) } customer shared, changes aloneStep 2: Avoid unbounded arrays and huge documents
A document has a size limit (16 MB), and very large arrays slow updates and indexes. Do not store an ever-growing list inside a parent document, such as every click for a user. Store each event as its own document that references the parent instead.
// risky: grows forever
{ _id: 1, user: "asha", clicks: [ ... millions ... ] }
// better: one document per event
{ _id: ObjectId(), userId: 1, url: "/pricing", at: ISODate("2026-10-10T10:00:00Z") }Step 3: Use common design patterns
The bucket pattern groups many small time-series measurements into one document per time window to cut document count. The subset pattern keeps only the most-read part (the latest ten reviews) inside the parent and the rest in another collection. The computed pattern stores precomputed values such as totals so reads stay fast. Pick a pattern only when you have a measured need. MongoDB also provides native time series collections for measurement data.
// bucket pattern: one document per sensor per hour
{ sensorId: "s1", hour: ISODate("2026-10-10T10:00:00Z"),
readings: [ { t: 0, v: 21.4 }, { t: 60, v: 21.5 } ], count: 2, sum: 42.9 }Step 4: Add schema validation
Validation lets you require fields and types while keeping flexibility elsewhere. Define a $jsonSchema validator when you create the collection or with collMod. Choose the validation action: error rejects invalid writes, warn only logs them.
db.createCollection("customers", {
validator: { $jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string", minLength: 1 },
email: { bsonType: "string", pattern: "^.+@.+$" },
age: { bsonType: "int", minimum: 0, maximum: 120 }
}
} },
validationAction: "error"
})Step 5: Plan for change
Add a schemaVersion field to documents so your code can handle old and new shapes while you migrate gradually. Make changes backward compatible where you can, and migrate in batches rather than one huge update. Design for your top queries: write them down before you design the documents.
FAQs
- Should I design collections like SQL tables?
- Not by default. Mirroring normalized tables forces many $lookup joins and gives up MongoDB's strength. Start from your application's queries and shape documents to serve them.
Summary
Embed data that is read together and bounded, reference shared or growing data, avoid unbounded arrays, use patterns such as bucket and subset when measured, enforce structure with $jsonSchema validation, and version your schema.
Related tutorials
- Databricks Module 8: SQL Warehouses, Performance, Cost, and a First Look at MLflow
- Databricks Module 7: Jobs, Orchestration, and Deploying with Git and Bundles
- Databricks Module 6: Declarative Pipelines and the Medallion Architecture
- Databricks Module 5: Ingesting Files with Auto Loader and COPY INTO
- Databricks Module 4: Unity Catalog, Volumes, and Governance
Comments
Log in or create a free account to join the discussion.