Event Sourcing
Event Sourcing is a design pattern in which all changes to the state of a system are captured as a sequence of immutable events. Instead of storing only the current state, the system persists every state-changing event, allowing the entire history to be reconstructed at any point in time.
By focusing on events as the primary source of truth, however, the current state needs to be reconstructed from the event history both for applying further changes to the system's state and for querying it. In combination with CQRS this is referred to as reconstruction of the write model, for applying state changes using commands, and projection of read models for querying, respectively.
Reconstructing the Write Model
The information needed to decide, whether a state change can be applied to the system, is referred to as write model.
In order to reconstruct it from the events stored within the system, not the entire event history is actually required. This is
due to the fact, that changes are applied to a specific instance of a domain object, e.g. a book copy. Thus, only those
events related to that specific instance, are actually needed to reconstruct its state, e.g. all event belonging to Book A
as shown below:
%%{init: { 'logLevel': 'debug', 'theme': 'default', "themeVariables": {
"cScale0": "olive",
"cScale2": "olive",
"cScale4": "olive"
} } }%%
timeline
Book A: BookPurchasedEvent
Book B: BookPurchasedEvent
Book A: BookLentEvent
Reader X: ReaderRegisteredEvent
Book A: BookReturnedEvent
Accordingly, sourcing events to reconstruct the write model for a specific domain object represents a filtered query of the entire event history, with the domain object identifier as the filter criteria. All relevant events are then combined (in order) to reproduce the write model, before any new changes are applied.
Info
As long as the maximum number of events per domain object is feasible, this operation can be performed on-demand resulting in an in-memory write model representation, no matter how big the entire event history actually is.
Projecting a Read Model
The goal of a read model is not to support command execution, but to expose the event history to the outside world in a suitable representation, the so-called projection. Generally, these come in two flavors:
- persistent state representations stored within a dedicated datastore for querying, e.g. a SQL database
- changes applied to other systems, e.g. mails sent to registered users or REST API calls made to third-party systems
Both have in common, that the projected data isn't limited to dedicated domain object instances. Therefore, the entire
event history has to be processed to project a read model. However, the projection may only be interested in certain
event types, e.g. all BookLentEvent
and BookReturnedEvent
instances to track the book availability.
Accordingly, read model projections usually use some kind of (persistent) progress tracking, in order to avoid re-projecting the entire event history repeatedly, both to avoid duplicate changes applied to other systems and to guarantee stable performance.