Event sourcing: Write event before or after updating the model
- by Magnus
I'm reasoning about event sourcing and often I arrive at a chicken and egg problem. Would be grateful for some hints on how to reason around this.
If I execute all I/O-bound processing async (ie writing to the event log) then how do I handle, or sometimes even detect, failures?
I'm using Akka Actors so processing is sequential for each event/message. I do not have any database at this time, instead I would persist all the events in an event log and then keep an aggregated state of all the events in a model stored in memory. Queries are all against this model, you can consider it to be a cache.
Example
Creating a new user:
Validate that the user does not exist in model
Persist event to journal
Update model (in memory)
If step 3 breaks I still have persisted my event so I can replay it at a later date. If step 2 breaks I can handle that as well gracefully.
This is fine, but since step 2 is I/O-bound I figured that I should do I/O in a separate actor to free up the first actor for queries:
Updating a user while allowing queries (A0 = Front end/GUI actor, A1 = Processor Actor, A2 = IO-actor, E = event bus).
(A0-E-A1) Event is published to update user 'U1'. Validate that the user 'U1' exists in model
(A1-A2) Persist event to journal (separate actor)
(A0-E-A1-A0) Query for user 'U1' profile
(A2-A1) Event is now persisted continue to update model
(A0-E-A1-A0) Query for user 'U1' profile (now returns fresh data)
This is appealing since queries can be processed while I/O-is churning along at it's own pace.
But now I can cause myself all kinds of problems where I could have two incompatible commands (delete and then update) be persisted to the event log and crash on me when replayed up at a later date, since I do the validation before persisting the event and then update the model.
My aim is to have a simple reasoning around my model (since Actor processes messages sequentially single threaded) but not be waiting for I/O-bound updates when Querying. I get the feeling I'm modeling a database which in itself is might be a problem.
If things are unclear please write a comment.