MongoDB Schema Design for SaaS Billing
A practical guide to modeling subscription billing in MongoDB.
Why MongoDB for SaaS Billing?
MongoDB's document model is a strong fit for billing data because subscriptions, invoices, and plans have nested, variable structures that map naturally to JSON documents. Relational databases require multiple JOIN tables for the same data — MongoDB keeps related billing entities in a single document, reducing query complexity and read latency. For a SaaS product, this means faster invoice generation, simpler plan changes, and easier audit trails.
Core Collections
A SaaS billing system typically needs four core collections: plans (subscription tiers with pricing, features, and intervals), subscriptions (active customer subscriptions linked to a plan), invoices (billing records with line items, status, and payment info), and credits (usage credits, promo balances, or refunds). Each collection stores embedded sub-documents rather than references — for example, an invoice embeds the line items directly instead of storing them in a separate table.
Plan Schema Design
The plans collection should store tier name, price in cents (avoid floats), billing interval (monthly/yearly), feature flags as a map of booleans, and metered fields like API call limits or storage caps. Use BSON Decimal128 or integer cents for all monetary values — never use floating-point numbers. Index the interval and isActive fields since most queries filter by active plans.
Subscription Schema
Each subscription document links a customer to a plan with start and end dates, status (active/past_due/canceled/trialing), and a nested currentPeriod object. Store the Stripe or payment provider subscription ID for reconciliation. The trick is embedding enough context — plan name, price at time of subscription — so invoice generation doesn't require joining back to the plans collection. This makes the subscription document self-contained for billing operations.
Invoice Schema with Embedded Line Items
Invoices should embed line items as an array of sub-documents, each with description, quantity, unit price in cents, and total. Top-level fields include customerId, subscriptionId, status (draft/paid/overdue/voided), dueDate, and totals. The embedded approach means fetching a single document gives you the complete invoice — no JOINs needed. Index by customerId and status for the most common queries: list unpaid invoices for a customer or find all overdue invoices.
Credit and Usage Tracking
For metered billing, create a usage collection with customerId, metric name, value, and timestamp. Use MongoDB's aggregation framework to sum usage over billing periods. Credits work similarly — store a balance document per customer and decrement atomically using $inc. Both patterns are simple to implement and perform well at SaaS scale when properly indexed.
Indexing Strategy
Key indexes for a billing system: compound index on subscription (customerId + status) for customer billing lookups, index on invoices (dueDate + status) for dunning workflows, unique index on payment provider IDs to prevent duplicates, and TTL index on stale invoices for automatic cleanup. Use MongoDB's explain() to verify query coverage before deploying to production.
Should I use embedded documents or references for billing data?
Embed when data is read together (invoices with line items) and rarely changes independently. Reference when data changes frequently or is shared across many documents (customer info referenced from invoices). For billing, invoices with embedded line items is the standard pattern.
How do I handle multi-currency billing in MongoDB?
Store all monetary values as integer cents with an ISO currency code field alongside. Convert to display amounts in your application layer. Avoid storing different currencies in the same field — keep amount and currency as a paired unit.
What's the best way to store recurring billing periods?
Store the start and end dates of each billing period directly on the invoice document. For active subscriptions, maintain a currentPeriodStart and currentPeriodEnd that update on renewal. This makes period-based queries trivially simple.
How to Build a SaaS MVP: Step-by-Step Guide
Ship your SaaS MVP in weeks, not months.
MongoDB vs PostgreSQL for SaaS: Which Database Should You Choose?
A head-to-head comparison of the two most popular databases for SaaS products.
Supabase vs Firebase 2026: Which Backend Platform Should You Choose?
Make an informed choice between the two leading backend-as-a-service platforms.
Let's work together
Have a project in mind?
From landing pages to full SaaS platforms — let's build something exceptional.