It's None of Your Business¶
Three weeks ago, we introduced Simple Mode, a new mode for EventSourcingDB that replaced expressive domain events with three universal operations: row-inserted, row-updated, and row-deleted. Many of you laughed. Some of you forwarded it to colleagues who were not sure whether to laugh or cry. But beneath the satire, the frustrations we exaggerated were real. Teams genuinely struggle with Event Sourcing, and the reason they give is almost always the same: "It's too complex. We just want to store data."
We have heard that sentence dozens of times. And every time, we have come to the same conclusion: the complexity is not the problem. The problem is a misunderstanding about whose job it is to make business decisions. Because right now, in codebases all over the world, developers are making those decisions every single day, quietly, implicitly, and almost always without realizing it. And that is where things go wrong.
The Hardest Part Is Not the Technology¶
In the Simple Mode post, we wrote a line that was meant as satire but turned out to be the truest sentence in the entire article: "The hardest part of Event Sourcing is not the technology. It is the relentless demand to understand your own business." We were joking. But people kept quoting it back to us, unironically, because it resonated.
Here is why. When a developer writes an event called row-updated with a payload of { status: 'unavailable' }, they have just made a business decision. They have decided that there is no meaningful difference between a book that is borrowed, a book that is damaged, a book that is being transferred to another branch, and a book that has gone missing. All of these are "unavailable." One string. Four completely different business realities, each with different consequences, different rules, and different downstream processes.
A borrowed book has a return date and a borrower. A damaged book needs assessment and possibly insurance. A book in transit has a destination and an expected arrival. A missing book triggers an inventory review. The developer collapsed all of that into a single field because nobody told them otherwise. And that is the key insight: they were not being lazy. They were not cutting corners. They were doing what developers do. They saw a problem, they found a solution, and they implemented it. The issue is that the problem they solved was a business problem, and they solved it without asking the business.
Consider what this looks like in code. This is the kind of event you see in systems where developers make business decisions on their own:
await client.writeEvents('/books/42', [
{
source: 'https://library.eventsourcingdb.io',
subject: '/books/42',
type: 'row-updated',
data: { status: 'unavailable' }
}
]);
Now compare it to what happens when a developer asks the domain expert "What does it actually mean when a book becomes unavailable?"
await client.writeEvents('/books/42', [
{
source: 'https://library.eventsourcingdb.io',
subject: '/books/42',
type: 'io.eventsourcingdb.library.book-transferred-to-branch',
data: {
destinationBranch: 'west-side',
expectedArrival: '2026-04-28',
requestedBy: 'staff-7'
}
}
]);
The second version did not require more technical skill. It required one conversation. The developer asked a question, got an answer, and wrote code that reflects what actually happens in the domain. The first version is a guess. The second version is knowledge.
When You Decide, You Assume¶
This pattern shows up everywhere, not just in library systems. Every time a developer chooses a data structure, names a field, or decides how to handle a business scenario, they are making an assumption about the domain. Most of the time, they do not even notice.
A customer cancels an order. The developer deletes the record. But the accounting department needs a cancellation invoice. The warehouse needs to know whether the items were already picked. Customer support needs the cancellation reason for their reports. DELETE captures none of this. OrderCancelled captures all of it, because it forces you to ask: what actually happens when an order is cancelled? Who needs to know? What are the consequences?
A user changes their shipping address. The developer overwrites the old one. But the logistics system already dispatched a package to the previous address. Now there is no record of where the package was actually sent. The old address is gone, replaced by the new one. In an event-sourced system, ShippingAddressChanged preserves both the old and the new address, along with the timestamp. The question "Where did we send the package?" remains answerable.
An admin deactivates a user account. The developer sets a flag or deletes the row. But GDPR requires that personal data be erased. Support needs to retain the ticket history. Compliance needs the audit trail. Three stakeholders, three conflicting requirements, and the developer just picked one without consulting any of them. An event like AccountDeactivated preserves the fact that something happened, while separate processes handle data erasure, ticket archival, and audit logging according to their own rules.
In each of these cases, the developer answered a business question with a technical decision. The answer felt natural, even obvious. But it was wrong, or at best incomplete, because the person best equipped to answer the question was never asked. Here is a pattern worth remembering: if you are unsure which event type to use, you have not discovered a technical problem. You have discovered a business question. And business questions deserve business answers.
Ask, Don't Interpret¶
The shift is surprisingly simple in theory and surprisingly hard in practice. It comes down to three steps.
First, recognize the business question. Every time you ask yourself "How should I implement this?", pause and consider: is the answer technical or domain-specific? "Which framework should I use for HTTP routing?" is a technical question. You can answer it yourself. "What happens when a book is returned late?" is a domain question. You cannot, or rather, you should not.
The distinction is not always obvious. "Should I use a boolean or an enum for the status field?" sounds technical. But it is actually a domain question in disguise. The answer depends on how many states the business recognizes, what transitions are valid, and what each state means. The data type is technical. The semantics behind it are not.
Second, ask instead of guessing. This feels uncomfortable at first. Developers are problem-solvers by nature. When they see an open question, their instinct is to resolve it. Asking someone else can feel like admitting you do not know what you are doing. But consider the alternative: a doctor who diagnoses without examining the patient. A lawyer who drafts a contract without understanding the deal. A translator who guesses at words they do not know. In every other profession, making assumptions about the domain you serve is considered malpractice. In software development, we call it velocity.
Ask the product owner. Ask the domain expert. Ask the person who actually does the work that your software is supposed to support. The conversation might take fifteen minutes. The wrong assumption might cost three sprints when someone finally notices that cancellations do not generate invoices, that address changes erase shipping history, or that deactivating an account also destroys the compliance trail.
Third, document the answer in your code. This is where Event Sourcing becomes more than a storage pattern. When you name an event BookReturnedLate instead of row-updated, you are encoding a business decision directly in the codebase. The event name is documentation. It tells the next developer what happened, why it matters, and that someone deliberately chose this name because the domain expert confirmed: yes, a late return is different from a regular return. As explored in Naming Events Beyond CRUD, the name you give an event determines whether your system tells a story or keeps a secret.
There is a common anti-pattern worth addressing directly: "I don't want to bother the product owner with every little decision." This is well-intentioned but backwards. You bother them far more when you implement the wrong behavior and they discover it three sprints later in a review, in production, or worse, from a customer complaint. A fifteen-minute conversation now saves days of rework later. The product owner would rather be asked than surprised.
A Shared Language Changes Everything¶
Something interesting happens when developers start asking domain questions regularly. A shared vocabulary emerges. Not because someone mandated it in a meeting or put it on a wiki page, but because people who talk to each other about the same things naturally converge on the same words.
Before the conversation, the developer says "status update" and the domain expert says "loan." They are talking about the same thing but using different words. After the conversation, both say BookBorrowed. The event name becomes the shared language. Not a glossary that nobody reads. Not a diagram that gets outdated after the first sprint. A living term that exists in the code, in the conversations, and in the domain expert's head, all at the same time.
In Domain-Driven Design, this concept is called Ubiquitous Language, and it is often presented as something you need to establish before you start coding. A workshop. A glossary. A modeling session. All of that can help, but it misses the simplest entry point: just ask what things are called, and then call them that in your code. As covered in DDD: Back to Basics, the ubiquitous language is not a document. It is a practice.
Event Sourcing enforces this practice in a way that CRUD systems do not. When your system expects events with meaningful names, you cannot get away with row-updated. The system pushes back. It asks: updated how? Why? What happened? You are forced to find a name that reflects the domain, and finding that name requires understanding the domain, and understanding the domain requires talking to people who know it. The technology creates a feedback loop that pulls you toward better domain understanding.
This is exactly what Simple Mode satirically destroyed. By reducing all events to row-inserted, row-updated, and row-deleted, it eliminated the mechanism that forces the conversation. And without the conversation, you are back to developers making silent assumptions. As we wrote in Hidden in Plain Sight: The Events You Forgot to Model, the events you overlook are often the ones that matter most. You overlook them because you never asked about them.
Start With One Conversation¶
If all of this sounds like a fundamental shift in how your team works, it is. But it does not have to happen all at once. You do not need a three-day Event Storming offsite. You do not need to remodel your entire domain. You do not need buy-in from every stakeholder. You need one conversation.
Here is what that looks like in practice. Open your codebase and find one place where you used a generic event type. Updated. Changed. Modified. Something that describes a data operation rather than a business occurrence. Now find the person who knows what actually happens at that point in the process. A product owner, a domain expert, a long-tenured colleague who has been doing this work for years. Ask them: "When this happens, what do you call it? What does it mean? What are the consequences?"
Then rename the event. row-updated becomes BookBorrowed. StatusChanged becomes LoanDefaulted. RecordModified becomes MembershipRenewed. Each new name is a small act of understanding. Each one makes the codebase a little more readable, a little more honest, a little more aligned with the business it serves.
Watch what happens next. The code becomes easier to read. New team members understand it faster. The next business question is easier to ask because you have already established that asking is normal, expected, even valued. One conversation leads to another, and another, and before long, your team has built a shared language without ever formally deciding to do so. That is how it works in practice. Not with a grand initiative, but with a habit. As Blueprinting the System explores through the lens of Event Modeling, the best designs emerge when everyone looks at the same picture. Your first conversation is the first brushstroke.
The title of this post is deliberately ambiguous. "It's none of your business" can mean "it does not concern you." But read differently, it also means: there is none of your business in your code. No domain knowledge. No shared understanding. No business language. Just technical operations masquerading as design decisions. The fix is not a new framework, a better tool, or a more sophisticated architecture. The fix is a conversation.
If you would like to explore how Event Sourcing, CQRS, and Domain-Driven Design work together to create systems that speak the language of the business, visit cqrs.com. And if you are in the middle of that shift and want to talk it through, reach out at hello@thenativeweb.io. We have been through this transition ourselves, and we are always happy to share what we have learned.