I was recently invited to chat at the Dead Code podcast (thank you for the invite!), about Event Sourcing and Ruby. Here are some notes (and corrections!) from the conversation.
I enjoyed the conversation and I hope it was useful to some people. In an attempt to pack as much context as possible I did get a bit ramble-y at times, and didn’t manage to touch on some important topics.
Corrections
-
I mentioned Jeremie Chassaing’s excellent blog post Functional Event Sourcing Decider, but I said the examples were in C# when I should have said F#. I was speaking out of tune, if you will 🥁.
-
I gave commands perhaps more emphasis than I should have. I’ve written about the command layer as a general abstraction before, and its different interpretations in Event Sourcing. But the role of commands is actually a bit of a hot topic in some quarters, and I should have focused a bit more on the “core” pattern around state and events.
-
I repeatedly referred to the notion of a “flat” mental model, in opposition to RDBMS-driven CRUD models that can often lead to “deep” object hierarchies with complex networks of inter-related concepts. I should have used the word “shallow” instead.
What I didn’t talk about
So much!
CQRS
While Event Sourcing on its own is only concerned with “sourcing” current state from events, CQRS provides the architectural scaffolding around it to make it usable in most systems. How do you query event-sourced data? How to you handle side-effects? How do you build UIs?
Eventual Consistency
You can definitely use Event Sourcing in immediately consistent systems, but where it really shines (in my opinion) is when you abandon the illusion of consistency across domain boundaries and embrace the eventually-consistent relationships between reality and software designed to represent it.
I briefly touched on the example of products in a warehouse getting stolen, lost or broken while the software still thinks they’re available to sell. It doesn’t matter how careful you are about your database transactions: reality will always get out of sync with your data, and Event Sourcing offers both a mental model and a mechanism to manage and compensate for those scenarios.
Concurrency boundaries
In most CRUD systems, concurrency is an implementation detail, usually handled in configuration. In Event Sourcing, concurrency is elevated to a first-class component of the mental model, because command handlers (or “deciders”, the bit of the architecture where you guard domain invariants and produce new events) exist as little islands of guaranteed consistency in a sea of eventual consistency (see above). This makes these components the natural “units of concurrency”. Not dissimilar from the Actor Model.
It’s in fact concurrency and eventual consistency that I’ve been trying to explore in my Sourced library, as early-stages as it is at the time of this writing.
Durable execution
Durable execution is the idea that you can split an operation into small idempotent steps, and persist the state of each step as it progresses. If one of the steps crashes, you can seamlessly retry the operation from the last persisted state “checkpoint”. Event Sourcing can lend itself for this kind of approach pretty naturally, as operations can be modeled as workflows where each “step” leaves behind a trail of events that can be replayed to reconstitute the state of the operation at any point in time.
Modeling and documentation
The way in which Event Sourcing can make the domain model “shallower” by decoupling concepts can also make it easier to model and diagram the domain before even writing any code. Instead of complex UML diagrams you can describe the entire behaviour of a system in terms of events that happen in the domain. This is the drive behind Event Storming and Event Modeling, and I recommend looking into those.
The inverse is also true: given the right affordances in your code, Event Sourcing can make it easier to generate diagrams and documentation from it. Also something I’ve been exploring in my code.
Some resources
- This is an excellent and short video series explaining the general ES/CQRS set of patterns.
- The DDD-CQRS-ES Discord is full of helpful people and fascinating discussions.
- The Understanding Event Sourcing book is an excellent introduction to implementing event-sourced systems with the help of Event Modeling.
- Async API is a specification and set of tools to help model and document event-driven systems in general. Think Open API but for events.
And the Ruby libraries I mentioned in the podcast: