For the tl;dr on Event Sourcing, scroll to the end. For the slightly longer version, continue…
When I first learned about Event Sourcing, I was totally lost. Granted, I was a brand-new, not yet coffee-addicted developer straight out of college without any practical, real-world experience. All I knew how to do was create an abstract class and sort a linked list, so when I was assigned an event sourced project, it took me a bit to get comfortable with the pattern. Two years later, I have seen what Event Sourcing is capable of and can confidently say I would use it again.
What is Event Sourcing?
Event Sourcing is a programming pattern that keeps an accurate record of changes to the system rather than the current state itself. These changes are stored as “events” that can be used to build a read model that will present the current state to the user.
Think about it like this: a system for a bank can show the current balance of a bank account (the current state) and every transaction taken to get there (the events). The bank’s system can always determine the current balance from a list of transactions, but not vice-versa. In an event sourced system, the preservation of the events is more important than the current state, as the current state can always be rebuilt from the events.
Event Sourcing & CQRS
I also found the CQRS pattern (Command Query Responsibility Segregation) when learning about Event Sourcing. CQRS uses separate operations to read from (“query”) and write to (“command”) the specified data store. This could be as simple as using different service methods to perform these operations. CQRS is a pattern separating reads and writes, while Event Sourcing is a pattern involving how the updates are applied. These patterns complement each other, but are distinct.
Event Sourcing and CQRS interact when the commands are processed and translated into events, then they are applied (“projected”) to create the most up-to-date version of the read model that can be queried. Understanding this “event lifecycle” left my head spinning, which was the precursor to drawing some diagrams for a colleague like the one below.
The Advantages of Event Sourcing
Built-in Audit Trail & Offline Concurrency Conflict Resolution
Given the right problem space, Event Sourcing can be a solution to issues that seem unrelated. One of the most significant advantages of this pattern is the built-in paper trail, which is an audit log of every action taken since the system’s initialization. Event Sourcing also provides a way to handle concurrency issues involving invalid data updates. For example, you have two offline systems queuing updates for when the system comes back online. This is possible by resolving conflicts as the updates are translated into events, as seen in the figure below. Normally, an audit log would not present an immediate solution for offline conflict resolution.
Generally, adding wrappers around basic CRUD database operations is fantastic because they can be written in the language of the domain. For a developer working in an industry they are not familiar with, being able to write code in the language of the industry is extremely important. Now add an audit log to the mix. How do you write out every action taken and ensure none are missing?
- Create a new table to keep track of actions.
- Add an extra database operation for every action taken.
- Maintaining the audit log has become a secondary process that is not critical to functionality. As a secondary workflow, it has to be tested independently of the associated action.
Using Event Sourcing:
- Create a new log entry (or event).
- Have the standard read model update based on additions to the log (event store).
- It is still two actions, but it prioritizes the maintenance of the log (event store) that then becomes the blueprint for the current state of the models. Testing is one workflow. The action the user takes requires an audit log entry in order to be reflected on the system.
Let’s put it this way: An event sourced system is built to be maintained and operated out of a detailed, domain-specific log.
Multiple Read Models for Querying
In addition to the audit log and concurrency conflict resolution, the event store can have multiple read models for querying. By adding more event handlers, there can be uniquely prepared read models for each consumer that would have required highly complicated and time-consuming queries of the event store to generate otherwise. More resources can be found on the use cases for Event Sourcing at the end.
Issues & Considerations
The biggest disadvantage of this pattern is the added layer of complexity. The added difficulty of defining events can hinder development teams and projects that don’t necessarily benefit from the advantages. There is also more overhead when fixing a poorly defined but already-released event, so being able to code in the domain-specific language of the business is a must-have when introducing Event Sourcing. For example, suppose you are using a system that is able to go offline, and Event Sourcing is being used to check for inconsistencies. In that case, there is also significant complexity in defining the rules around resolving and reconciling those conflicts.
Lastly, a part of maintaining the event store is ensuring it does not get into an invalid state. Enforcing business logic before events are produced is a critical part of any implementation of Event Sourcing. If a bad event is created, it is much more difficult to correct the problem in a way that does not affect the downstream projections.
The primary advantages of Event Sourcing:
- Built-in audit trail (and ability to make temporal queries)
- Offline concurrency conflict resolution
- Domain-specific events
- Allowing multiple custom projections or read models for reporting purposes
And the difficulties to consider:
- Extra time to spin up the environment resources to manage the event store
- Complexity that will require some training for onboarding developers
- Increase in developer time to ensure event sourced models and their events are built on correct domain-specific logic
- Conflict resolution when an offline system is using an outdated version of the event store or read model
- Enforcing business logic on any action performed before creating events
Below are some resources that I found useful when learning about Event Sourcing: