Your Read Model Doesn't Always Need a Database - EventSourcingDB
Skip to content
Initializing search
Getting Started
Fundamentals
Deployment and Operations
Reference
Client SDKs
Extensions
MCP Server
Best Practices
Implementation and Development
Data Management and Performance
Operations, Compliance and Infrastructure
Common Issues
Blog
Categories
Privacy Policy
Legal Notice
Your Read Model Doesn't Always Need a Database¶
Sooner or later, every team building an event-sourced system runs into the same question: which database should we use for the read model? The textbook answer is reassuringly pragmatic. It depends. Pick the tool that fits the access pattern. Relational if you need joins and ad-hoc queries, document-oriented if your data is hierarchical, key-value if you mostly look things up by ID, a graph database for relationships, a spatial database for geodata, and so on.
That advice is sound, and yet it quietly assumes something that nobody ever questions: that there has to be a database in the first place. There's one option that almost never makes it onto that list, and it's the one that turns the whole question on its head. For a large class of read models, you don't need a database at all. You can keep the entire read model in memory and serve it straight from there.
The Question Everyone Asks¶
The polyglot-persistence answer is good advice, but it answers a narrower question than the one people think they're asking. "Which database fits this access pattern?" is a useful question once you've established that you need a database. It says nothing about whether you need one.
In a system backed by a relational database as the single source of truth, the question makes sense, because that database is your data. There's nothing to fall back on. If you lose it, you lose everything, so it has to be durable, transactional, and carefully chosen.
An event-sourced system is different in a way that changes the entire calculation. The events are the source of truth. Everything else is derived from them. And a read model is exactly that: something derived. That single property is what opens the door we're about to walk through.
A Read Model Is Not Your Source of Truth¶
It's worth being precise about what a read model actually is, because the word "database" carries a lot of baggage. A read model in an event-sourced system is a projection. It's the result of folding a stream of events into whatever shape your queries happen to need. It holds no information that the event store doesn't already have.
That makes a read model disposable by design . You can throw it away and rebuild it from the events at any time. You can have ten different read models of the same events, each shaped for a different screen, and none of them is more "correct" than the others. If this framing is new to you, our post on Your Aggregate is Not a Table makes the same point from the write side, and CQRS Without the Complexity explains why separating the read side from the write side is what makes all of this possible in the first place.
Once you internalize that a read model is derived and disposable, the requirements you'd normally put on a database start to look negotiable. Durability stops being a hard constraint, because the durable copy lives somewhere else. And that's precisely the point where keeping the read model in memory becomes a reasonable engineering choice rather than a reckless one.
"But RAM Is Volatile"¶
This is the first objection, and it's a fair one. Memory doesn't survive a restart. If your process goes down, the in-memory read model is gone.
The thing is, that's fine, because the read model was never the thing you couldn't afford to lose. When the process starts again, it replays the events from the event store and rebuilds the read model from scratch. For most systems this takes seconds, maybe a little longer for large histories. The read model is briefly unavailable while it catches up, and then it's back, identical to what it was before. A rebuild is not data recovery. It's just the read model doing the only thing it ever does.
In practice you also don't run a single instance. You run several, behind a load balancer, each with its own copy of the read model in memory. They don't all restart at the same moment, so from the outside the read model stays available even while individual instances cycle. And if an instance reconnects after a disconnect, EventSourcingDB lets it resume from the ID of the last event it saw, so it doesn't have to start from zero every time. The volatility of memory turns out to be a non-issue once the durable truth lives in the event store.
"But It Won't Fit in Memory"¶
The second objection is about size, and this one deserves a clear-eyed look rather than a reflexive dismissal. Some read models genuinely are too large to hold in memory, and for those you should absolutely use a database.
But take a moment to estimate before you...