Notes based on playing and researching the Event Sourcing & Command Sourcing.
This is closely related to microservices if applied properly.
Event sourcing and command sourcing
- Good segregation of application logic in terms of data modification and querying
- This should simplify reasoning about the application logic
- Since all modifications are just a stream of events it avoids side-effects people tend to put into read logic
- Also allows to have optimal models for each of above
- Improved scalability - write part is append-only so it reduces locking complexity
- If read model is updated asynchronously then not very well suited for complex screens where user can edit many fields and assumes upon save to get refreshed data
- Well suited for task oriented UI
- Commands - requested actions to be performed on the model, should be named as verbs
- Commands are immutable - they live just for short time of transfer from caller to command handler
- Events - performed model state changes, should use past tense (something happened and it cannot be undone)
- Events are immutable - past cannot be changed
- Stored in a sequential log which can be later used for analysis of what happened in the system
- Can be used later to update for example querying model
- Fixing a state change should be done by adding another event which reverts the previous operation
Dealing with concurrency
- Pretty simple:
- Event can contain ‘version’ of model which is being updated
- Event log then can have a unique constraint for each aggregate id and model version
- Assuming two or more concurrent actions updated the same aggregate on same version one of the updates will fail
- Command handler then can decide:
- Retry the same operation with some merge logic
- Fail the operation
- This constraint can be implemented in relation database as well as other services (like Google Firebase).
Operations which require multiple aggregates
- In monoliths usually a transaction covering multiple tables
- In microservices this would usually mean at least distributed transaction
- Often this is just not possible since some systems simply do not support it or even worse - have just limited or no transactional support
- This can be to some degree solved by saga pattern
- Either flow of events processed asynchronously as the individual services progress in processing
- Or performed from one place as a sequence of steps which subsequently update states
- Defining right aggregate
- This is crucial since each aggregate then has own event log etc.
- Aggregate should never perform operations outside itself
- As result in relation database this wouldn’t need FK constraints
- What belongs to command handler vs. event handler?
- This seems to be sometimes a bit unclear and even various sources have various requirements - e.g. where belongs sending an email - to command handler or to event handler?
- Rebuilding model from events each time may be slow - some kind of caching may be needed - memory cache can be used for this.
- Dealing with structural changes of commands and events due to bugfixing etc.