A factual comparison of TerminusDB and MongoDB for developers evaluating document databases. Both store JSON documents, but they make fundamentally different architectural trade-offs.
Executive summary
MongoDB is a general-purpose document database designed for high-throughput, horizontally scalable workloads. It stores BSON documents in schemaless collections and excels at operational data — web applications, content management, real-time analytics.
TerminusDB is a document graph database with built-in version control. It stores JSON documents in a schema-enforced graph, tracks every change as an immutable commit, and supports branch, diff, merge, and time-travel. It is designed for collaborative, auditable data workflows.
The key architectural difference: MongoDB is a mutable database optimised for write throughput and horizontal scale. TerminusDB is an immutable database optimised for read throughput at scale, data integrity, collaboration, and full change history.
Comparison table
| Aspect | TerminusDB | MongoDB |
|---|---|---|
| Data model | JSON documents in a schema-enforced graph | BSON documents in collections |
| Schema | Enforced on every transaction (mandatory) | Optional validation rules (off by default) |
| Relationships | First-class graph links, traversed without JOINs | Manual references or embedded documents |
| Query language | WOQL (Datalog) + Document API + GraphQL | MQL (MongoDB Query Language) + Aggregation Pipeline |
| Version control | Built-in: branch, merge, diff, time-travel | Not built-in (Change Streams for event capture) |
| Immutability | Append-only delta layers | Mutable in-place updates |
| ACID transactions | Full ACID on every write | Multi-document ACID (since 4.0) |
| Scalability | Single-node with delta compression | Horizontal sharding (native) |
| Schema migration | Schema weakening (backward-compatible) | No built-in migration; application-managed |
| Audit trail | Every commit: author, message, timestamp | Requires Change Streams + custom storage |
| Deployment | Docker container, ~50 MB | mongod binary or Atlas (cloud) |
| Licence | Apache 2.0 | SSPL (Server Side Public License) |
| Graph traversal | Native WOQL path() and triple patterns | $graphLookup (limited recursive lookup) |
| Secondary indexes | Schema-defined fields indexed automatically | Manual index creation required |
Data model
MongoDB: flexible documents
MongoDB stores documents as BSON (binary JSON) in collections. There is no enforced relationship between documents — you choose between embedding (denormalisation) or referencing (normalisation):
// Embedded approach (denormalised)
db.orders.insertOne({
customer: { name: "Alice", email: "alice@example.com" },
items: [{ product: "Widget", price: 9.99, qty: 2 }],
total: 19.98,
status: "processing"
})
// Referenced approach (normalised)
db.orders.insertOne({
customer_id: ObjectId("..."),
items: [{ product_id: ObjectId("..."), qty: 2 }],
total: 19.98,
status: "processing"
})MongoDB does not enforce that customer_id points to an existing customer. Referential integrity is an application-layer responsibility.
TerminusDB: schema-enforced document graph
TerminusDB stores JSON documents with enforced schema. Relationships are typed links that the database validates:
{
"@type": "Order",
"customer": "Customer/alice",
"items": [{"@type": "OrderItem", "product": "Product/widget", "quantity": 2}],
"total": 19.98,
"status": "processing"
}The schema guarantees that customer points to a valid Customer document and product points to a valid Product. Invalid references are rejected at write time. Because documents are stored as a graph, you traverse relationships directly in queries — no $lookup aggregation stages needed.
Schema enforcement
MongoDB: opt-in validation
MongoDB collections are schemaless by default. You can add JSON Schema validation, but it is optional:
db.createCollection("orders", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["customer_id", "total", "status"],
properties: {
total: { bsonType: "number", minimum: 0 },
status: { enum: ["pending", "processing", "shipped", "delivered"] }
}
}
}
})This validates document shape, but does not enforce referential integrity between collections.
TerminusDB: mandatory schema
TerminusDB requires a schema. Every document type is defined as a class with typed properties:
{
"@type": "Class",
"@id": "Order",
"customer": "Customer",
"items": {"@type": "Set", "@class": "OrderItem"},
"total": "xsd:decimal",
"status": "OrderStatus"
}The database enforces this on every transaction — you cannot write a document that violates its class definition. This catches data integrity issues at write time rather than at query time or in production.
Version control and history
This is the most significant architectural difference between the two databases.
TerminusDB: git for data
Every write in TerminusDB is an immutable commit:
# Every API call records author and message
curl -X POST "http://localhost:6363/api/document/admin/shop/local/branch/main?author=alice&message=Add+new+order" \
-d '{"@type": "Order", "customer": "Customer/alice", "total": 19.98, "status": "pending"}'You get branch, diff, merge, time-travel, and clone — built into the database. See Version Control Operations for the full API reference.
MongoDB: no built-in version control
MongoDB overwrites documents in place. To achieve version history, you would need to:
- Change Streams — capture real-time change events (available since 3.6)
- Custom versioning — store version numbers and previous versions manually
- Event sourcing — build an append-only event log at the application layer
Each approach requires significant application-level engineering. MongoDB does not natively support branching, diffing, or time-travel queries.
Querying relationships
MongoDB: $lookup and $graphLookup
MongoDB requires explicit $lookup stages in the aggregation pipeline to join documents across collections:
db.orders.aggregate([
{ $lookup: {
from: "customers",
localField: "customer_id",
foreignField: "_id",
as: "customer"
}},
{ $unwind: "$customer" },
{ $match: { "customer.city": "London" } }
])For recursive relationships, $graphLookup provides limited graph traversal but lacks the expressiveness of a native graph query language.
TerminusDB: native graph traversal
TerminusDB traverses relationships directly — no join stages needed:
WOQL.and(
WOQL.triple("v:Order", "rdf:type", "@schema:Order"),
WOQL.triple("v:Order", "customer", "v:Customer"),
WOQL.triple("v:Customer", "city", WOQL.string("London")),
WOQL.triple("v:Order", "total", "v:Total")
)For multi-hop relationships, WOQL's path() operator handles recursive traversal natively — no special syntax or pipeline stages.
When to choose MongoDB
- High-throughput web applications — MongoDB's write throughput and horizontal sharding suit high-traffic operational workloads
- Flexible, evolving schemas — when you genuinely need schema-free storage during rapid prototyping
- Large-scale analytics — MongoDB Atlas provides aggregation pipelines, Atlas Search, and time-series collections
- Existing ecosystem — mature drivers for every language, extensive tooling (Compass, Atlas, Charts)
- Horizontal scaling — when you need to shard across many nodes for write-heavy workloads
- Full-text search — Atlas Search (built on Lucene) provides integrated text search
When to choose TerminusDB
- Collaborative data workflows — teams editing shared datasets with branch/merge review
- Regulated industries — finance, healthcare, compliance requiring full audit trail on every change
- Schema-first development — when the database should enforce integrity, catching errors at write time
- Complex relationships — when your data has deep interconnections that MongoDB's
$lookuphandles awkwardly - Data versioning — knowledge bases, reference data, catalogues that evolve over time and need history
- Data lineage and reproducibility — scientific data, financial reports, regulatory filings
- Lightweight deployment — single Docker container with no operational overhead
Worked example: managing a product catalogue
Suppose you need a product catalogue where changes are reviewed before going live.
In MongoDB
You would implement a review workflow at the application layer:
// Draft collection for pending changes
db.product_drafts.insertOne({
product_id: ObjectId("..."),
changes: { price: 14.99 },
status: "pending_review",
author: "alice@example.com",
created_at: new Date()
})
// Application code handles approval and applies changes
// No built-in diff, no history, no rollbackYou need to build the entire review, diff, and audit system yourself.
In TerminusDB
The review workflow uses built-in version control:
# Create a branch for catalogue changes
curl -X POST http://localhost:6363/api/branch/admin/catalogue/local/branch/price-review \
-d '{"origin": "admin/catalogue/local/branch/main"}'
# Make changes on the branch
curl -X PUT "http://localhost:6363/api/document/admin/catalogue/local/branch/price-review?author=alice&message=Update+widget+price" \
-d '{"@id": "Product/widget", "@type": "Product", "name": "Widget", "price": 14.99}'
# Reviewer sees exactly what changed (structural diff)
curl -X POST http://localhost:6363/api/diff/admin/catalogue \
-d '{"before_data_version": "main", "after_data_version": "price-review"}'
# Response: [{"@id": "Product/widget", "price": {"@op": "SwapValue", "@before": 9.99, "@after": 14.99}}]
# Approve and merge
curl -X POST http://localhost:6363/api/apply/admin/catalogue/local/branch/main \
-d '{"before_commit": "main", "after_commit": "price-review", "commit_info": {"author": "bob", "message": "Approve price update"}}'
# Full history preserved — time-travel to any previous stateNo custom review system needed. Diff, history, rollback, and audit are built in.
Summary
MongoDB and TerminusDB both store JSON documents, but solve different problems:
- MongoDB excels at high-throughput operational workloads with flexible schemas and horizontal scaling
- TerminusDB excels at collaborative, auditable data workflows with enforced schemas and built-in version control
They are not interchangeable — choose based on whether your primary challenge is scale and throughput (MongoDB) or data integrity, collaboration, and history (TerminusDB).