state are stored as a sequence of events. Not just can we query these events, we can also use the event log to reconstruct past states, and as a foundation to automatically adjust the state to cope with retroactive changes. (Fowler, 2005)
Events track a change in state • The order of events is essential • Track the version of an event, track when an event occurred (VCS) • Events are append-only • History must not be re-written • Use compensating events to correct issues (AccountBilled > AccountRefunded)
Design complement each other well • Events should be part of the ubiquitous language • Domain events are an ideal stepping stone • Aggregates and Entities are ideal candidates • No setters allowed
e.g. Account • If necessary, include the name of the sub-entity/value object, e.g. AccountInvoice • Describe what exactly has happened • Use past tense, e.g. AccountInvoiceGenerated • Include all the properties modifying the state of the aggregate/entity in the payload of the event • Good: AccountCreated, AccountInvoiceGenerated, CustomerPaymentVerified • Bad: DomainObjectUpdated, AccountInserted, AccountInvoiceEvent
locking prevents any concurrent access • Safe, but slow • Optimistic locking tries to detect concurrent access • Keep track of the version of an aggregate • Throw an exception if there’s a mismatch between the aggregate and the event version • Further conflict resolution possible
If possible, use proven technology or a NoSQL database • https://geteventstore.com • https://www.mongodb.com • If possible, use one collection/stream per aggregate instance • Think about sharding • Serializers can be a bottleneck • Performance can degrade over time
aggregate • Create them at regular intervals • Can be created on-the-fly using a trigger condition • Can be created in the background using a scheduled job • Start without it and see how far you can get • Once necessary, start with larger intervals
we're only interesting in the first version if (!$event instanceof AccountClosed) { return []; } // transform to the current version $upcasted = new AccountClosedV2( $event->accountId(), ‘no reason given', $event->status(), $event->customer(), $event->occurredOn() ); return [$upcasted]; }
easy • No ORM required • Getting Event Sourcing right (the first time) is not an easy task • Invest in monitoring, logging • KPI: # of events, # of events/s, # of events per aggregate, … • It requires a different mind set • It might require different infrastructure • It might require refactoring your business logic
a pattern that I first heard described by GregYoung. At its heart is the notion that you can use a different model to update information than the model you use to read information. For some situations, this separation can be valuable, but beware that for most systems CQRS adds risky complexity. (Fowler, 2011)
Emits events when application state changes • Events are dispatched asynchronously (RabbitMQ, ZeroMQ) • Query side with multiple different read models (MySQL, MongoDB, ElasticSearch, …) • Be aware of eventual consistency
intend: • Command: state -> command -> event • Event: state -> event -> state • Commands have to be persisted before they are executed • Might not be side-effect free
Sourcing is hard without CQRS • CQRS offers good Performance/Scalability • Complexity and learning curve are challenges • Separation of concerns should be a side effect • Only apply CQRS where it’s most valuable