Event Sourcing & CQRS
Problem
When interacting with data, most applications store the current state of the data.
Updates to the data elements are typically overwritten by the latest.
If one wanted a history of all the updates, the application would need to be built to maintain history tables.
Every time an update request is made, the current data would be moved to the history table before being overwritten.
This adds to the overhead of having a robust historical backup and ensuring the application scale plan includes scaling the historical stores as well.
Solution
Event Store
How it works
Define application change as a sequence of events and record them in an event store in the sequence they were applied (an audit trail)
The events are immutable and stored as append-only but can be published to consumers who may process them as required
This model not only stores the history of the state of the application but also allows a replay of those events to obtain the current state of the application — one of the following
A complete rebuild
A point in time rebuild
Reverse events
The trick here is dealing with external systems in a manner where they do not know the difference between real transactions and replays
example
Event sourcing works a lot like Book Keeping: A bookkeeper will log the day’s financial transactions such as payments, receipts, purchases etc made by an organization and document it within either a supplier or general ledger
All the financial events, money-in and money-out are now available in these ledgers (..the audit trail)
An accountant can use this information to create reports for any custom time-period
Going through the transactions (replay), the accountant could potentially recreate the same report multiple times and still get the same result for that specified period
Rehydration
process of rebuilding the current state of an entity by replaying all of its events from the event store
Event versioning
once events are persisted, they become immutable (they’re your system’s historical record). Over time, your domain may evolve (ie schema changes)
SHould not rewrite previous events
this will lose historical record
How
Keep old event class around
Use the event_type (or a version field in the event payload) to determine which version of the class to deserialize to.
Add a version Field in the Event Payload
Add a version field in JSON:
Use Custom Deserialization / Migration Logic
you can use Jackson’s @JsonCreator and @JsonProperty to handle optional fields
For the missing fields, will set to null, and use that to fill in defaults
Migrations
Code first migration
Keep V1 events in the DB, deserialize them with V1 classes, and apply them.
New events use V2 class & serialization.
The event data is json
Database Migration (If Absolutely Necessary)
Optimistic Concurrency Control (OCC)
ensure consistency in a concurrent environment—especially when multiple users or services try to modify the same aggregate simultaneously
OCC ensures that no two processes can apply conflicting changes without noticing.
assumes that conflicts are rare and handles them after they occur rather than preventing them before.
How
Here’s how it works conceptually:
When you load an aggregate, you also get a version number (often the count of events applied to it).
When saving a new event, you include that version number.
The event store checks:
If the current version in the store (after rehydration) matches the expected version ➡️ proceed with save.
If not ➡️ concurrency conflict ➡️ fail or retry.
Links
https://youtu.be/i2eVTk2Fb40
https://codurance.com/2015/07/18/cqrs-and-event-sourcing-for-dummies/
https://martinfowler.com/eaaDev/EventSourcing.html
https://axoniq.io/resources/concepts
https://completedeveloperpodcast.com/episode-134/
Event Sourcing - You are doing it wrong – David Schmitz https://www.youtube.com/watch?v=UevquaeEJ8c
https://eventmodeling.org/
Event Modeling from Beginner to Expert • Adam Dymitruk • YOW! 2023 https://www.youtube.com/watch?v=Pin_B-AbdXE
Last updated
Was this helpful?