Paradise CodeSoftware Studio
Back to articles
Backend EngineeringUpdated 16 min read

MongoDB Schema Design for Multi-Tenant SaaS

Embed vs reference decisions, tenant modeling, compound indexes, and patterns that survive growth and reporting.

MongoDBSaaSschemamulti-tenantdata modeling

Ali Mortazavi

Founder, Paradise Code

In MongoDB, schema means access patterns

Document orientation is not permission to be messy. Each collection should form around frequent product questions: “What does the tenant dashboard read?” “How is the order list filtered?” If you model like normalized tables and then join in the app, you keep relational complexity and lose document advantages.

Before modeling, write the product’s top ten queries. The schema is the answer to that list.

Embed vs reference

Embed fits data read/written with the parent and bounded in growth: user addresses, line items on a fresh order. Reference fits independent, fast-growing, or multi-parent entities: products, users, files.

The 16MB document limit is a design ceiling, not just a safety rail. Unbounded arrays (event logs inside a user doc) hurt later. For unlimited growth, use a child collection keyed by parent id.

Multi-tenant modeling

The common pragmatic model: one cluster, `tenantId` on every document, leading indexes on `tenantId`. Per-tenant databases belong to enterprise isolation or strict compliance needs—they multiply operational cost.

Every query must enforce tenant scope. In the repository layer, make tenant filters non-bypassable so IDOR bugs cannot leak through the data model. Keep shared platform data (plans, translations) in separate collections without tenant keys.

Indexes that keep SaaS alive

Build compound indexes with a tenant prefix: `{ tenantId: 1, createdAt: -1 }`, `{ tenantId: 1, status: 1, updatedAt: -1 }`. Uniqueness is usually compound too: email unique per tenant, not necessarily globally—unless the product says otherwise.

TTL indexes control temporary data (sessions, OTP codes, raw events). Find unused indexes with `$indexStats` and drop them; writes are not free.

Transactions, consistency, and distributed reality

Multi-document transactions exist, but they are expensive and should not be every write. Prefer a model where one document is the unit of truth for an operation. When multiple documents must change, define invariants and compensating actions clearly.

For counters and inventory, use atomic document operations or a queue/outbox; race conditions in paid SaaS become financial damage quickly.

Growth, archival, and reporting

Separate hot and cold data. Keep analytics events off the online path; send them to a pipeline or archive collection. For tenant reporting, nightly pre-aggregation or change streams beat heavy aggregates on user requests.

Version the schema seriously: optional new fields, gradual migration, defensive reads in code. An unversioned schema locks change by month six.

Model checklist before launch

Write down each collection’s owner, top ten queries, indexes, array growth ceilings, and delete/TTL policy. If any of those are vague, the schema is not SaaS-ready.

A good data model is invisible—until a bad one is not. Early investment here is cheaper than next year’s rewrite.

Frequently asked questions

Should we always use MongoDB transactions?

No. Prefer atomic single-document models first. Reserve transactions for true multi-document invariants.

Is embedding addresses inside a user correct?

If the set is bounded and read with the profile, yes. If addresses are independent entities with history or sharing, reference them.

One database per customer?

Only for special isolation/compliance needs. For most SaaS, `tenantId` with indexes and access control is enough and more operable.

How do we prevent document growth issues?

Move unbounded arrays to child collections, add TTL/archival, and check growth ceilings in model review.

Insights

Need these ideas implemented in your product?

Paradise Code supports you from consult to full delivery.

Request collaboration