The Essence of Event Sourcing
Type “event sourcing” into Google Images and you’ll be flooded with fancy diagrams: event buses, event stores, read and write databases, CQRS, arrows and blocks everywhere. My intention isn´t to say these diagrams are technically wrong—they often reflect real architectures, but I believe they draw attention away from the essence of event sourcing. We can get so distracted by the complexity of these architectures that we miss what, in my opinion, is the simple but powerful idea underneath: storing events instead of just the current state—an idea that, unlike those complex setups, is applicable to systems of all sizes.
And just to back this up with a bit more authority, let’s look at how Martin Fowler introduces the topic. Here’s the very first paragraph from his blog post on Event Sourcing:
We can query an application’s state to find out the current state of the world, and this answers many questions. However there are times when we don’t just want to see where we are, we also want to know how we got there.
I guess it’s safe to say that Martin Fowler would reserve the first paragraph for the essence of the topic, right? And in this case, the essence is clear: storing events — how we got there — instead of just the final state — where we are. My small contribution here, and really the main goal of this text, is to emphasize that this doesn’t need to be a big architectural shift. It can be applied at a smaller scale, as a powerful modeling technique.
With that said, let’s get closer to code, then. Every state
or status
field you store might be hiding concepts that should have been made explicit in your model. Every event you choose not to capture is information you’re choosing to lose. It’s easy to overlook, but many problems we attribute to code quality or bad requirements actually stem from poor design—unclear, inadequate models that fail to capture what really matters.
Let’s go even deeper and work through a concrete example. Suppose you’re building a ticketing system and decide to store a single status
field—Pending
, In Progress
, Done
. Every time someone updates the status, you lose what came before. How long did the ticket stay in In Progress
? Who moved this ticket to Done
? For which reason was this ticket Reopened
and by whom ? See the problem ? That’s a poor model. It may work for a while, but how long until questions like these start to surface?
Now, it’s worth seeing how much richer and expressive an event-based model can be. If you decide to store events instead of a calculated status
column, things might look like this:
TicketCreated
byManager
onDec 8th, 12:03:40 AM
TicketMarkedAsDone
byDev A
onDec 9th, 1:40:07 PM
TicketReopened
byQA
onDec 10th, 9:11:25 AM
due toRegression Tests Failed
TicketReassigned
fromDev A
toDev B
onDec 10th, 10:22:14 AM
Looks, professional, right? Every action is recorded. Every event can carry specific metadata. With this approach, you’re not just tracking where the ticket is— you’re preserving, in Martin Fowler’s words, how it got there.
Let’s conclude by being repetitive — and by emphasizing what really matters. What we explored here is simple—and applicable to every system we build in our day-to-day work. Yes, world-class, high-scale systems might require event buses or specialized event stores. Yes, some architectures split write and read databases—a pattern known as CQRS (which is often confused with, but distinct from, event sourcing). But those are optimizations for rare, specific contexts. The core idea—storing events instead of just final state—is much more universal. And the good news is: you don’t need any fancy stack to benefit from it.