Don't Kill Your Users¶
When I consult customers on Event Sourcing, there's a moment that happens almost every time. The team looks confident, maybe even a little proud. They tell me: "Actually, we already do Event Sourcing." And then they show me their events. UserCreated. OrderUpdated. InvoiceDeleted. My heart sinks a little.
This is not Event Sourcing. This is CRUD with event syntax. It has the shape of events but none of the semantics. And that distinction, subtle as it may seem, is the difference between a system that speaks the language of your business and one that speaks the language of databases.
If you're curious about our Event Sourcing consulting, this is exactly the kind of thing we help teams work through. But let me show you what I mean with an example.
The Enterprise Print Management Example¶
Imagine a customer who deals with enterprise printing. Their software manages thousands of printers across large organizations: hospitals, universities, corporate offices. Printers need to be added to the system, taken offline for maintenance, paused, resumed, and eventually removed when they're decommissioned.
The team built what they call an "event-sourced system." Here are their events:
That's it. Three events. Sound familiar?
This is CRUD wearing a costume. The names have changed from INSERT, UPDATE, DELETE to Created, Updated, Deleted, but the semantic poverty is identical. When you look at a PrinterUpdated event, what do you know? Something changed. What changed? You have to dig into the payload. Why did it change? No idea. Was it a planned maintenance action or an unexpected failure? The event doesn't tell you.
The Murder Problem¶
Let me take a brief detour with a darker example, and I hope the irony is clear.
Consider UserDeleted.
Read that out loud. UserDeleted. It sounds like you successfully murdered the user. "The user has been deleted." If this were a crime novel, the detective would be taking notes right now.
But what you probably meant was AccountClosed. Or UserDeactivated. Or SubscriptionCancelled. Each of these has a different meaning, different business implications, different downstream effects. But CRUD language collapses all of that richness into a single, vaguely morbid verb.
This isn't just imprecise. It's actively misleading. CRUD language doesn't just lose information. It can make your events sound like a crime report.
What Is Missing: The Maintenance Question¶
Back to our printers. A maintenance technician calls the help desk. "This printer on the third floor isn't working. What happened?"
The support agent looks at the current state in the database:
That's all they see. The printer is offline. But why? When did it go offline? Was it manually taken offline for maintenance, or did it fail unexpectedly? Has this happened before? How often?
With CRUD events, the agent has to dig through logs, correlate timestamps, maybe call the technician back later. Every minute spent investigating is money lost, frustration gained.
This is the hidden cost of semantic poverty. Not just lost information, but lost time, lost context, lost trust in the system.
The Semantic Alternative¶
What would real events look like? Events that capture what actually happened in the language of the domain?
Notice the subtle but important shift. It's PrinterAdded, not PrinterCreated. Why? Because the printer wasn't "created" in any meaningful sense. Nobody manufactured a printer by inserting a database row. The printer already existed. It was a physical object sitting in a box, shipped from a factory. What happened was that it was added to the system.
Created is CRUD language. Added is domain language. The difference matters.
Similarly, Removed instead of Deleted. The printer wasn't destroyed. It's probably sitting in a storage closet somewhere, waiting to be reassigned. It was removed from this system's management. That's what happened. That's what the event should say.
The Complete History¶
Now let's replay that support call with real events. The agent pulls up the printer's history:
2026-01-15 09:00 PrinterAdded
2026-01-15 09:05 PrinterBecameOnline
2026-01-16 14:30 PrinterPaused { "reason": "scheduled maintenance" }
2026-01-16 16:00 PrinterResumed
2026-01-17 11:45 PrinterWentOffline { "reason": "paper jam detected" }
2026-01-17 12:00 PrinterBecameOnline
2026-01-18 09:22 PrinterWentOffline { "reason": "connection lost" }
Now the agent can see everything:
- The printer went offline today at 09:22 because the connection was lost.
- This is the second unexpected offline event in two days.
- Yesterday it was a paper jam, today it's connectivity.
- Before that, there was a planned maintenance pause that went smoothly.
The technician doesn't need to troubleshoot blindly. They know to check the network connection, not the paper tray. They can see a pattern forming, maybe this printer's network card is failing.
This is the value that CRUD events cannot deliver. Not just "what is the state" but "how did it get there" and "what does this pattern tell us."
Events as Natural Language¶
This connects to something deeper. In a previous post, I wrote about how nobody tells stories with CRUD. You don't say "Little Red Riding Hood was created, then updated, then the grandmother was deleted." That's absurd. You tell the story through events: what happened, in what order, and why it mattered.
And in another post about DDD, I argued that Domain-Driven Design has been made unnecessarily complicated. The essence is simple: speak the language of the domain. But we've buried that simplicity under layers of patterns and jargon.
Here's the connection: when you formulate good events, you automatically speak the language of the domain. You can't write PrinterPaused without understanding that pausing is a concept in this domain, distinct from going offline, distinct from being removed. The act of naming events forces you to have conversations with domain experts. "What do you call it when a printer stops working temporarily but intentionally?" That question leads you straight into the heart of the domain.
This is DDD without calling it DDD. No aggregates, no bounded contexts, no intimidating terminology. Just events that capture what happens, named the way the business names them.
The Path to DDD Leads Through Events¶
Event Sourcing is a Trojan horse for Domain-Driven Design.
Explaining DDD to customers is hard. The terminology is intimidating. The concepts are abstract. Many developers (myself included) have felt "too stupid" for DDD at some point, overwhelmed by the theory.
But explaining events is easy. Everyone understands events. "What happened?" is a question humans have been asking since language began. And when you push teams to name their events precisely, they end up doing DDD naturally. They start speaking the language of the domain. They start having conversations with domain experts. They start building systems that reflect reality instead of reflecting database tables.
So if you're looking at your events and seeing Created, Updated, Deleted everywhere, you have an opportunity. Not just to improve your event model, but to start a conversation about what actually happens in your business. What do you call it when a printer is added? When a user leaves? When an order is cancelled versus refunded versus disputed?
The answers to those questions will tell you more about your domain than any pattern catalog ever could.
Where to Go From Here¶
If this resonates with you, start here: look at your events. Read them out loud. Do they sound like database operations, or do they sound like things that happen in your business?
For a deeper dive into the concepts behind Event Sourcing, the Introduction to Event Sourcing explains the fundamentals. If you're ready to experiment, the Getting Started guide will have you writing events in minutes.
And if you want help transforming CRUD events into domain events, or if you're just not sure where to start, reach out at hello@thenativeweb.io or check out our Event Sourcing consulting.
Because your events should tell a story. Not a crime report.