Event Sourcing: A matter of definition
Event Sourcing is a straightforward pattern to both use and understand. However, why do we encounter numerous strong opinions, challenges, or even failures? The primary reason is usually due to confusion. In this article, I will attempt to elaborate on my thoughts by demystifying the definitions in order to mitigate this confusion.
The Definition
Thanks to the community's advocacy, and particularly the efforts of Oskar Dudycz, we can observe a decrease in the level of confusion between Event Sourcing and Event Streaming or Event Driven. Sometimes, it's also easier to grasp a pattern when we initially learn when it should not be applied.
To make it concise, Mathias Verraes's definition is exceptionally clear:
A system is Event Sourced when:
the single source of truth is a persisted history of the system's events;
and that history is taken into account for enforcing constraints on new events.
If the Event Sourcing definition is well-understood, we are already halfway there. However, to reach that point, it's also necessary to understand what an Event Store is in practice. Event stores are essentially key-value databases with features such as atomic writes, optimistic concurrency or idempotency. The key usually consists of a name and an ID, while the value is an ordered list of events. This key-value pairing is commonly referred to as a stream aka history.
Stream's life-cycle
Keep it short! Which means short enough to be able to read the entire stream without any performance issues. To achieve this, prior modeling is crucial. Event Modeling is an excellent method to discover and describe a system using an example of how information has changed within them over time. During the modeling we can define boundaries and independent streams therefore how to keep them short. It's always less expensive to refine or redo a modeling rather than realizing it was a mistake after the implementation or the release.
If this is not possible, my two cents is to avoid Event Sourcing altogether! It's worthless. Big streams are typically anti-patterns and are disguised as CRUD operations, cumulative data or command sourcing.
To clarify this by giving an example, auditing is one of the outcomes of Event Sourcing, but we don't do Event Sourcing solely for the purpose of auditing. So it shouldn't be used for updating entities info. There are other suitable patterns for this.
Snapshots technique is too complex as well and must be at the very bottom of the decision tree.
State VS Projections
The final boss! Which is the confusion between the state and projections. if we aren't familiar with earlier definitions, the failure might occur swiftly. But this one can heart slowly. It's hidden behind many tricks and fixes. However it's an easy boss to fight because it's again a matter of definition.
State
The state is private. It’s built only in-memory each time we load the history. That’s all! As said before It's used to enforce constraints on new events. Jérémie Chassaing's explanation is quite straightforward:
Reduce the history by evolving each event
state -> event -> state
, to get the current state.Decide
command -> current state -> events
, to get the command's behavior as new facts.Append new events to the history.
Projections
On the other hand, projections are public and it's very important to point out that there could be many of them. Every time new events are appended to history, projections are triggered right after. Like the state the history is replayed, but this time, it's in order to know how to update the read model.
What's interesting with projections and so read models, is that what is projected is concise and simple and doesn't contain all the internal state and not necessary data. They are optimized for the view and it's cheap to have many of them. No need to have a complex relational databases.
Common misuses are to project the state, to project from one event and not from the history, to not optimize the read models for views, or to make a canonical or monolithic model.
Conclusion
Event Sourcing is a straightforward pattern to both use and understand. However, it's crucial to grasp the definitions, regardless of implementation details.
Subscribe to my newsletter
Read articles from Amin Khansari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Amin Khansari
Amin Khansari
🌳🦎 Passionate about socio-technical architecture, defensive design and simple boring sustainable λ code.