Over the past few months, we have received more feedback than ever before. Hundreds of conversations with developers, architects, and CTOs have painted a remarkably consistent picture. The message was clear, and we owe it to you to be honest about what we heard: Event Sourcing is too complex. Not in theory. In theory, everyone loves it. But in practice, teams struggle. They struggle with modeling, with naming, with the sheer cognitive overhead of thinking in events rather than rows.
We have spent years advocating for expressive event models, for Domain-Driven Design, for capturing business intent rather than data mutations. We wrote about naming events beyond CRUD, about why your aggregate is not a table, about versioning events without breaking everything. We believed in all of it. But belief does not pay the bills if your team spends three months on event modeling before writing a single line of production code.
We Heard You¶
The feedback was surprisingly uniform across industries and team sizes. "We love the idea, but we just want to store data." "Why do I need five different event types for a simple book lending operation?" "My team spent an entire quarter debating whether it should be BookBorrowed or LoanInitiated." "We miss the simplicity of INSERT, UPDATE, DELETE."
One CTO told us: "I sent my team to a three-day Event Storming workshop. They came back with 200 sticky notes and zero clarity." Another wrote: "We tried to model our library system properly. We ended up with BookAcquired, BookCataloged, BookShelved, BookBorrowed, BookReturned, BookReserved, BookDamaged, BookRepaired, BookDecommissioned. That is nine event types for a book. A book. We just wanted to know which books we have and who borrowed them."
We kept hearing the same thing: the hardest part of Event Sourcing is not the technology. It is the relentless demand to understand your own business. And frankly, who wants that? Understanding your domain means talking to domain experts, challenging assumptions, and discovering that the way things actually work is far more nuanced than anyone thought. That is exhausting. Developers want to write code, not attend workshops.
So we made a decision. A big one.
Three Events Are All You Need¶
Today, we are introducing Simple Mode, a new mode for EventSourcingDB that strips away all the complexity of event modeling and replaces it with something every developer already knows: CRUD.
Simple Mode supports exactly three event types:
row-insertedfor creating new datarow-updatedfor changing existing datarow-deletedfor removing data
That is it. No more agonizing over event names. No more ubiquitous language. No more Event Storming workshops with sticky notes covering every wall in the office. Three operations. Universal. Simple. Done.
But we did not stop there. Simple Mode does not actually store events at all. Let that sink in for a moment. There is no event history, no append-only log, no immutable sequence of facts. Each subject holds exactly one record. An insert creates it. An update overwrites it. A delete removes it. Just like a row in a relational database. Because that is what everyone wanted all along, is it not?
Let us revisit the library example that we have used across many of our blog posts. In a traditional event-sourced approach, lending a book involves a carefully crafted chain of domain events: BookAcquired, BookBorrowed, BookReturned, each carrying specific business meaning, each telling part of a larger story.
With Simple Mode, you do not need any of that. A book enters the system? row-inserted. Someone borrows it? row-updated, which overwrites the previous record entirely. They return it? row-updated again, overwriting once more. The book is lost? row-deleted, and it never existed. No need to think about what "borrowing" means in your domain. No need to distinguish between a book being returned and a book being returned late. No need to capture business intent. Just overwrite the record and move on. After all, who would ever want to know why something changed when you can simply know what the current value is right now? And "right now" is a generous term, since you cannot even tell when the current value was written. But that is a feature: less metadata means less complexity.
Enabling Simple Mode¶
Getting started is as simple as adding a single flag:
Once enabled, Simple Mode automatically restricts operations to row-inserted, row-updated, and row-deleted. Any attempt to write an event with a domain-specific type will be rejected. This is a feature, not a limitation. It protects your team from accidentally doing domain modeling.
Writing data becomes refreshingly straightforward:
await client.writeEvents('/books/42', [
{
source: 'library-app',
subject: '/books/42',
type: 'row-inserted',
data: {
title: '2001: A Space Odyssey',
author: 'Arthur C. Clarke',
status: 'available'
}
}
]);
When someone borrows the book, simply send a row-updated with the new data. The previous record is gone. There is no history of the book ever having been available. But why would you need that? You know it is borrowed now, and that is all that matters. If someone asks "When did this book become available?", you can confidently answer: "I have no idea, and neither does the database." That is the power of simplicity.
No commands, no aggregates, no business rules. Just data in, previous data out. Just like a relational database, but without all the ceremony. And if you realize a week later that you actually needed a condition field on your books but never included one, simply do what every developer has loved doing since the dawn of relational databases: the equivalent of an ALTER TABLE. Go through every record, add the missing field, and hope you guessed the right default value. This is a well-understood process that has never caused any production incidents, ever.
You Do Not Need What You Think You Need¶
We know what you are thinking. "But what about all those features that Event Sourcing and EventSourcingDB provide?" Let us address them one by one. You will find that every single one of them is something you absolutely do not need, and that the workaround you will inevitably build to replace it is actually the superior solution.
Audit trail? You do not need it. But if your compliance department insists, simply write a log entry to a separate file every time you overwrite a record. Developers have been doing this for decades, and it works perfectly fine. Yes, the log might get out of sync with the actual data. Yes, it is a separate system you need to maintain. Yes, it does not provide cryptographic proof that the history has not been tampered with, something EventSourcingDB gives you out of the box. But it is familiar, and familiarity is more valuable than correctness. We used to think that built-in audit trails were a core advantage of EventSourcingDB. We were wrong. A text file is fine. Building and maintaining a separate logging infrastructure that duplicates what EventSourcingDB does natively feels much more like real engineering anyway.
Interpreting data in different ways? In a traditional event-sourced system, you can read the same stream of events and build different views from them: a list of available books, a borrowing history per member, overdue statistics, popular genres. With Simple Mode, you do not need any of that, because there is only one way to look at the data: the current record. If you need a different perspective, simply build a second system that copies the data and transforms it. Or a third. Or a fourth. Each with its own synchronization logic, its own consistency problems, and its own on-call rotation. Maintaining four tightly coupled systems that each hold a partial copy of the truth is a well-established architectural pattern that has never caused anyone to wake up at 3 AM. We wrote about CQRS without the complexity once. With Simple Mode, you can have CQRS with all the complexity you can handle, by building every part of it yourself.
Distinguishing different business operations? Since Simple Mode only stores the current record, there is nothing to search through. But if you really need to know what happened in the past, add a lastOperation field to your payload. Set it to borrowed when someone borrows a book, returned when they return it. Of course, this only tells you the most recent operation, since the previous value was overwritten. But that is fine. If you need a full history, build a separate changelog table and write to it on every update. Yes, this is exactly what an event-sourced system gives you for free. But doing it manually, and then debugging it when the changelog and the actual data inevitably diverge, is what separates a senior developer from someone who simply uses the right tool for the job. Solving problems that do not need to exist is an underappreciated art form.
A Clean Slate¶
Migrating to Simple Mode is remarkably straightforward. Since the entire point of Simple Mode is that you do not need history, all existing events are simply deleted during migration. Every carefully crafted domain event, every business fact you recorded, every audit trail you built up over months or years: gone. Clean. Fresh.
We know this might sound drastic. But think about it: if you are switching to a mode that treats data as disposable and overwritable, why would you keep the old data around? That would be inconsistent. And consistency matters, even if it means deleting everything.
Besides, let us be honest: other systems lose data all the time. Migration scripts that silently drop columns. Database upgrades that corrupt indexes. Cloud providers that have "incidents." We are just being transparent about it. Where others lose your data accidentally, we delete it on purpose. That is not a bug. That is a feature. We call it Intentional Data Liberation.
If you are worried about losing important information, we recommend exporting your data to a CSV file before migration. You can store it on a USB drive, put it in a desk drawer, and never look at it again. Just like everyone does with backups.
Happy April Fools' Day¶
If you have read this far with growing horror, we have good news: none of this is real. There is no Simple Mode. There is no --enable-simple-mode flag. Your events are safe, your history is intact, and we have no intention of turning EventSourcingDB into a glorified key-value store.
But here is the thing: everything we described as a "workaround" in this post is something that teams actually do in systems that do not support Event Sourcing. They bolt on audit logs that drift out of sync. They build backup tables for historical queries. They store the last operation as an untyped string in a generic record. They maintain multiple tightly coupled copies of the same data to get different views. They patch data manually when something goes wrong and pray the migration script works. They build, by hand and at great expense, what an event-sourced system provides by design.
If you read our post from just last week, Naming Events Beyond CRUD, you know why CRUD-style events are a trap. They hide business meaning behind generic labels. They make your event store unreadable. They throw away the most valuable thing your system can capture: what actually happened and why.
Event Sourcing is not simple. Understanding your domain is not simple. But the alternative, pretending that you do not need to understand your domain and then building elaborate workarounds when reality catches up, is far more expensive in the long run. The complexity does not disappear when you ignore it. It just moves somewhere harder to find.
If you would like to explore what expressive event modeling looks like in practice, our Getting Started guide is a good place to begin. And for a deeper dive into the concepts behind Event Sourcing, CQRS, and Domain-Driven Design, visit cqrs.com.
If this post made you laugh, or think, or both, we would love to hear from you at hello@thenativeweb.io. And yes, your events are still safe. We promise.