Bi-Temporal Event Sourcing, who cares?
Everybody wants to control time and change history. Even the software. Event Sourcing gives a reliable history of facts during a monotonically increasing timeline. Although people want to change history, it is not possible, despite many attempts to do so.
However, in software, as well as in life, we make conclusions based on the facts we have at certain points in time. If later, we learn new facts that happened earlier, we may change our previous conclusions.
Many people wrote about temporality: Mathias Verraes, Martin Fowler, and others. There are even some database solutions that support bi-temporality: XTDB, Rails EventStore, etc. I'd definitely recommend reading these articles to get familiar with the topic.
If there are so many resources, why am I writing about it? I personally think there is a lot of ambiguity about what it means for the Event Store to support bi-temporality. Will Event Store break its guarantee and rewrite history once it finds out about facts (events) that happened before the latest event in the Event Store? Let's find out!
Bi-Temporal Event Sourcing
Before we begin, let's quickly sum up what bi-temporal Event Sourcing is. Essentially, there are two timelines: system timeline and valid timeline. Events on the system timeline are ordered by the time the system found out about them. A valid timeline keeps events in order they actually happened in the world (see Diagram 1).
In this example, we have a bike rental application. Users can rent a bike but also can report a broken bike. Both of them have a field describing the exact (valid) time in the "real world" when they occurred. The upper diagram shows the ordering of events based on the system timeline - the order in which the system has found events. Firstly, the bike was rented out and then it was reported broken. However, the valid time of the "bike reported broken" event is before the valid time of the "bike rented out" event. The system shouldn't have rented out a broken bike!
So what?
The system already decided to rent out the broken bike. Would inserting the "bike reported broken" event before the "bike rented out" event fix the fact that the bike was rented out? Of course not. Compensating the renting action would.
Just by following the event stream, we are going to learn about these facts and we are going to adjust our Command and Query models accordingly.
What's the use of a valid timeline?
Another example that comes to mind is the collection of card transactions by the end of the day. Let's say that we record all transactions for the current day after midnight (basically the next day, or some other day when they are ready). The valid time of these transactions would be the day they actually happened. We already saw that if we have Query Models that are always tracking the event stream they are going to learn about the transactions in the correct (valid) time.
In cases when we want to retroactively provide a different card transaction report for the day that happened a few years ago, it would be beneficial not to replay all events in the Event Store*. Also, if we don't durably persist Query Models, we'd like to build their states on-the-fly. Anyway, we are talking about replay performance here. In those cases, it would be great if Event Store could select events based on a valid timeline fast.
In order to support faster reading based on the valid timeline, Event Store should build a temporal index for the valid timeline. It shouldn't rearrange the event stream, or store two streams ordered by different timelines.
Conclusion
Bi-temporality is just a different view of the same event stream. It has nothing to do with changing history, inserting events in the past (or future), etc. Event Store could support faster reading by implementing a temporal index for the valid timeline, but it's just an optimization.
* We need to replay almost all events since we are not sure when the system stopped receiving transactions for the specific card.
Comments
Post a Comment