UserDeleted, Diagnosis Murder: When software sounds like a crime scene

The language in which we describe processes determines whether a software system tells stories or police reports.

listen Print view
WĂĽrfel mit Kreuz auf Tastatur

(Image: stockwerk-fotodesign/Shutterstock.com)

9 min. read
By
  • Golo Roden
Contents

When I advise clients on Domain-Driven Design (DDD) and Event Sourcing, there's a moment that regularly occurs. The team seems confident, perhaps even a little proud. They tell me:

“Actually, we're already working with events.”

And then they show me their events:

  • UserCreated
  • OrderUpdated
  • InvoiceDeleted

And internally, I flinch.

the next big thing – Golo Roden
the next big thing – Golo Roden

Golo Roden is the founder and CTO of the native web GmbH. He works on the design and development of web and cloud applications and APIs, with a focus on event-driven and service-based distributed architectures. His guiding principle is that software development is not an end in itself, but must always follow an underlying technical expertise.

Because these aren't events, but classic CRUD, just with event syntax. They have the outward form of events but no inner semantics. And this difference, as subtle as it may seem, is the difference between a system that speaks the language of the business and one that speaks the language of databases.

Let's imagine a client dealing with enterprise printing. The software manages thousands of printers in large organizations: in hospitals, universities, and corporate offices. Printers need to be added to the system, taken offline for maintenance, paused, resumed, and finally removed when they are decommissioned.

The team has built something they call an “event-driven system.” And these are their events:

  • PrinterCreated
  • PrinterUpdated
  • PrinterDeleted

That's it. Three events. Does that sound familiar?

This is CRUD in disguise. The names have changed from INSERT, UPDATE, and DELETE to Created, Updated, and Deleted, but the semantic poverty is the same. When you look at a PrinterUpdated event, what do you know? Something has changed. But what exactly has changed? For that, you have to look into the payload. Why did it change? No idea. Was it scheduled maintenance or an unexpected failure? The event doesn't say.

And this always reminds me of a somewhat grim example (and the irony is hopefully obvious): Consider UserDeleted (which I also encounter regularly in practice).

Say it out loud and consciously: UserDeleted. It sounds like you've successfully murdered the user:

“The user has been eliminated.”

If this were a crime novel, the investigators would now be taking notes.

But what was likely meant was: AccountClosed. Or UserDeactivated. Or SubscriptionCancelled. Each of these has a different meaning, different business implications, different downstream effects. But CRUD language squeezes all this richness into a single, slightly morbid verb.

This is not just imprecise. It is actively misleading. CRUD language doesn't just lose information. It can make events sound like a police report.

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:

{ "printerId": “printer-3f-01”,
“status”: “offline”
}

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 now has to sift through logs, correlate timestamps, perhaps call the technician back later. Every minute spent on investigation means lost money and growing frustration.

These are the hidden costs of semantic poverty: not just lost information, but lost time, lost context, lost trust in the system.

What would real events look like? Events that capture what actually happened? In the language of the domain?

  • PrinterAdded
  • PrinterBecameOnline
  • PrinterPaused
  • PrinterResumed
  • PrinterWentOffline
  • PrinterRemoved

Notice the subtle but important shift. It's PrinterAdded, not PrinterCreated. Why? Because the printer wasn't “created” in any meaningful way. No one manufactured a printer by inserting a database row. The printer already existed. It was a physical object, sitting in a box, delivered from a factory. What happened was: it was simply 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 room somewhere, waiting to be reassigned. It was removed from the management of this system. That's what happened. That's what the event should say.

Let's play out the support call with real events now. 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 they 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 scheduled maintenance pause that went smoothly.

Consequently, the technician doesn't have to blindly experiment. They know they need to check the network connection, not the paper tray. They can recognize a pattern because the same problem has occurred with other printers in the past: perhaps the network card on this printer is failing.

This is the value that CRUD events cannot provide. Not just “what is the state,” but “how did it get there” and “what does this pattern tell us.”

This touches on something more fundamental. 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 Grandma 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: those who formulate good events automatically speak the language of the domain. You can't write PrinterPaused without understanding that pausing is a concept in that domain, distinct from going offline, distinct from being removed. The act of naming events forces conversations with domain experts. “What do you call it when a printer temporarily but intentionally stops working?” This question leads directly to 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 as the business names them.

In other words, Event Sourcing is a catalyst for Domain-Driven Design.

Explaining DDD to clients is difficult. The terminology is intimidating. The concepts are abstract. Many developers (myself included) have at some point felt “too dumb” for DDD, overwhelmed by the theory.

But explaining events is easy. Everyone understands events. “What happened?” is a question people have been asking since language existed. And when you get 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 rather than database tables.

So, if you look at your events and see Created, Updated, and Deleted everywhere, you have the opportunity not only to improve the 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 canceled versus refunded versus disputed?

The answers to these questions reveal more about the domain than any pattern catalog ever could.

If this sounds familiar, 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 insight into the concepts behind DDD and Event Sourcing, these introductions to Domain-Driven Design and Event Sourcing explain the fundamentals.

Because events should tell a story, not a police report.

(rme)

Don't miss any news – follow us on Facebook, LinkedIn or Mastodon.

This article was originally published in German. It was translated with technical assistance and editorially reviewed before publication.