Commands Aren't Just Events in Reverse - 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
Commands Aren't Just Events in Reverse¶
BorrowBook and BookBorrowed. ReserveSeat and SeatReserved. AcquireBook and BookAcquired. The first half is what someone wants, the second half is what happened, and the only visible difference is the tense. Most diagrams in most Event Sourcing tutorials draw the two as a pair, and after enough examples, your brain quietly starts assuming they always will be.
That picture gets people started, and it isn't wrong, exactly. But the more real your system becomes, the less it matches the shape of what's happening underneath. Commands and events look symmetric on the surface, and they're not. They're not symmetric in whether they can fail. They're not symmetric in who they speak to. They're not even symmetric in number. Treating them as if they were is one of the quietest, most expensive mistakes we see , because the code keeps working long enough for the asymmetry to set in everywhere before anyone notices.
The Picture That Looks Right¶
The reason this misconception is so durable is that it's incredibly useful at the beginning. When you teach Event Sourcing to someone, you reach for the simplest possible example. A book gets borrowed. A seat gets reserved. A payment gets made. In all of those, the command and the event line up perfectly. BorrowBook produces BookBorrowed. ReserveSeat produces SeatReserved. The shapes match, the names rhyme, and the diagram fits on a slide.
Tutorials lean on this for the same reason that physics lectures start with frictionless planes. The simplification gets the idea across. The trouble is that frictionless planes don't exist, and neither do systems where every command produces exactly one event of exactly the right name. The first time you build something for real, friction shows up everywhere, and the symmetric picture quietly stops paying its rent.
The tense trick reinforces the illusion. A command in imperative form, an event in past tense. BorrowBook becomes BookBorrowed, and it feels like the only thing that changed is when. That feeling is the whole bug. The shift from intent to fact is not a tense change. It's a transition across three different dimensions at once, and we'll look at each of them.
One Can Fail, the Other Cannot¶
The first asymmetry is the one most developers eventually feel, even if they never name it. Commands can be rejected. Events cannot. A command is a request that the system is allowed to refuse. An event is a record of something that already happened, and nothing can unsay it.
This is why one of the most common anti-patterns we see is CommandFailed sitting in the event store. Somebody hit a validation error, somebody sent a bad payload, somebody triggered a rate limit. Now there's an "event" in the log that doesn't describe anything that happened in the business. It describes that the system failed to accept a request. That's not a domain fact. That's infrastructure noise that found its way into the wrong place.
The rule needs a sharper edge, though, because not every failure is noise. Some failures are facts about the business, and they deserve to be remembered. When a card payment is declined, that's not the system being broken. The cardholder has to be told, the merchant has to react, the risk model has to learn. PaymentDeclined is a perfectly legitimate event because it's something the domain itself cares about. The same goes for AccessDenied when someone tries to enter a building they shouldn't, or LoanRefused when underwriting says no.
The distinction is between business failure and technical failure. Business failures are part of the story your system is telling, and they belong in the event log alongside the successes. Technical failures are signals about the system itself, and they belong in logs, metrics, or error responses, not in the store. If the failure tells a business story, it's an event. If it's just the system arguing with itself, it isn't.
One Has an Addressee, the Other Has an Audience¶
The second asymmetry is about direction. A command is sent to someone. There is a specific recipient, usually a specific Aggregate, that's expected to handle it and produce a specific outcome. The sender knows who they're talking to. The receiver knows what they're being asked. The conversation is one-to-one.
Events are not like that at all. An event is published, not sent. It's broadcast into the world for anyone who finds it interesting. Sometimes nobody is listening. Sometimes one projection is listening. Sometimes five different Bounded Contexts are listening, each interpreting it for...