Siddhant DevalAuthor
Senior Full-Stack Engineer·Jul 12, 2026·17 min read
Document & NoSQL Databases: MongoDB & DynamoDB in Production
Document databases trade join-ability for write flexibility. This article covers the embedding vs. referencing decision, DynamoDB single-table design, event sourcing with Streams, schema versioning discipline, cost modeling at scale, and GDPR-compliant erasure strategies for denormalized document stores.
Technical Series
Modern Database Paradigms
Part 3 of 8
Document & NoSQL Databases: MongoDB & DynamoDB in Production
Selecting the right database is a foundational architectural decision; data gravity ultimately dictates the scalability and resilience of an application — choose the paradigm first, the product second. The promise of document databases is seductive: no rigid schema, rapid iteration, flexible structure. The broken pattern is believing that "schemaless" means "schema-free." Every document store develops an implicit schema — it is just encoded in the application layer where no database constraint can enforce it, and every migration must be written by hand. This article shows you how to use document databases correctly: when the flexibility earns its cost, and when you are simply moving the rigor problem from the database to the application.
1. The Document Model Mental Model
A document database stores each entity as a self-contained JSON document. The design decision that dominates everything else is: embed or reference?
javascript
1.1 Embedding vs. Referencing Decision Matrix
| Dimension | Embed | Reference |
|---|---|---|
| Cardinality | One-to-few (< 100 items) | One-to-many, one-to-millions |
| Update frequency | Child rarely changes independently | Child updated independently of parent |
| Query pattern | Parent and child always fetched together | Child queried independently or in batches |
| Atomicity need | Single-document atomicity sufficient | Multi-document transactions needed |
| Document size growth | Bounded, predictable | Parent stays small |
Mental Model Check
Embed when the child entity has no life outside the parent — an order's line items, a profile's address list. Reference when the child entity is independently queryable, independently updated, or unbounded in count.
2. Data Modeling
2.1 MongoDB Schema Versioning
The "schemaless" label is the most dangerous myth in document databases. Without explicit versioning, schema drift turns a document collection into an archaeology site:
javascript
2.2 DynamoDB Single-Table Design
DynamoDB requires all access patterns to be defined before the first write. The key insight is that a single table, with carefully chosen partition and sort keys, can serve multiple entity types:
typescript
Performance / Safety Warning
Retrofitting access patterns onto a DynamoDB table with an existing key design is expensive — it often requires a full table scan and re-write via DynamoDB Streams or a batch migration. Design the key schema for ALL access patterns before the first write hits production.
2.3 The 16MB Document Limit
javascript
3. MongoDB Aggregation Pipeline
The aggregation pipeline is MongoDB's equivalent of SQL's
GROUP BY, JOIN, and window functions — but sequential, document-stream-based:javascript
Architectural Note
MongoDB's
$lookup (join) is expensive relative to a Postgres JOIN — it does not use indexes from the foreign collection efficiently. For join-heavy reporting workloads run at high frequency, Postgres is faster. Reserve aggregation pipelines for analytical queries run on a schedule or on demand, not for every page render.4. DynamoDB Streams — Native Event Sourcing
typescript
Pro Tip & Optimization
DynamoDB Streams remove the need for a separate Kafka or SQS bus for document-mutation triggers. The stream is ordered per partition key, retained for 24 hours, and can fan out to up to 2 Lambda consumers. For event volumes exceeding Lambda concurrency limits, route through Kinesis Data Streams.
5. Atlas Vector Search — MongoDB as a Combined Store
For teams already running MongoDB who need RAG search at moderate scale (under 5M vectors):
javascript
6. Cost Model at Scale
| Scenario | DynamoDB On-Demand | DynamoDB Provisioned | MongoDB Atlas (M30) |
|---|---|---|---|
| 1M reads/day + 100K writes/day | ~$2.10/day | ~$0.40/day (pre-provisioned) | ~$6/day (flat) |
| 10M reads/day + 1M writes/day | ~$21/day | ~$4/day | ~$6/day (flat — same tier) |
| 100M reads/day + 10M writes/day | ~$210/day | ~$40/day | ~$40/day (M50 tier) |
Crucial Requirement
DynamoDB's on-demand pricing is convenient but expensive at sustained high throughput. Switch to provisioned capacity with auto-scaling once your traffic is predictable — it is typically 3–8× cheaper. MongoDB Atlas pricing is compute-based (instance tier), so cost is flat until you upgrade the tier.
7. Security
7.1 DynamoDB Fine-Grained Access Control
json
This IAM condition restricts each Cognito-authenticated user to only read/write DynamoDB items where the partition key matches their own Cognito identity — enforced at the AWS API layer, not the application layer.
7.2 MongoDB Atlas Field-Level Encryption
typescript
Crucial Requirement
GDPR Compliance: Document databases store denormalized copies of user PII across multiple documents. Implement a
userId compound index on every collection containing PII, and a scheduled erasure job that bulk-deletes all documents by userId. Do not rely on TTL for compliance erasure — TTL is for expiry, not for right-to-erasure. For DynamoDB, create a GSI on userId as the erasure scan key. Document your data map (which collections contain which PII fields) before your first compliance audit.8. When NOT to Use Document / NoSQL
| Scenario | Why Document Fails | Better Alternative |
|---|---|---|
| Complex multi-entity joins at query time | $lookup is expensive; no query planner optimization across collections | PostgreSQL — B-tree indexes, JOIN optimizer |
| Strong ACID across multiple documents | Multi-document transactions in MongoDB exist but carry significant write overhead and limit horizontal scaling | PostgreSQL — native multi-statement transactions |
| Arbitrary reporting / ad-hoc analytics | Aggregation pipeline requires knowing the query shape upfront; slow for exploratory queries | PostgreSQL + analytics view, or a dedicated OLAP store |
| Strictly typed, constraint-enforced data | Schema validation exists in MongoDB but is opt-in and not enforced at the storage level | PostgreSQL — typed columns, CHECK constraints, FK enforcement |
Summary
| Concept | Rule |
|---|---|
| Embedding vs. referencing | Embedding optimizes for read performance; referencing optimizes for write flexibility — choose based on the dominant operation. |
| DynamoDB key design | DynamoDB single-table design requires modeling all access patterns upfront — retrofitting is expensive and often requires a full migration. |
| Schema discipline | Schema evolution in MongoDB requires explicit versioning discipline — 'schemaless' enables chaos, not speed. |
| DynamoDB Streams | DynamoDB Streams eliminate a separate event bus for document-mutation triggers in serverless architectures. |
| When to switch | Document databases are wrong for complex reporting, multi-entity ACID transactions, and workloads requiring ad-hoc joins. |
What's Next
In Part 4, we cover distributed SQL — how CockroachDB solves PostgreSQL's horizontal scaling ceiling with Raft consensus, and the cross-region write latency, serializable isolation overhead, and clock skew failure modes you must understand before committing.
Research & Synthesis Note
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.
#MongoDB#DynamoDB#NoSQL#Document Database#Single-Table Design#Schema Design#GDPR
Technical Series
Modern Database Paradigms
Part 3 of 8