5

I'm thinking about whats the correct pattern to handle this case in CQRS / Event Sourcing:

I created events to calculate a balance and system goes into production.
By accident, this implementation has a bug and calculation of balance is wrong.

Should I fix the implementation and rewrite the events in the event store as well as the snapshots?
Do event store implementations provide this out of the box or do I have to solve this at the underlying DB level?

At a wider scope: what if event signature is changing (e.g. more fields added or fields renamed)?

I found these slides saying rewrite is the way to go: https://ookami86.github.io/event-sourcing-in-practice/#slide-51

  • Can you add more detail on the object and event types? surely the event which calculates a balance, is stored as an updateBalance to event rather than the calculation? – Ewan Jun 26 '16 at 17:00
  • I don't have a particular scenario in mind at the moment. Thanks for your explanation. – Alexander Zeitler Jun 28 '16 at 18:31
2

OK im going to add a quick answer, really need more detail on your particular case to explain i think.

You should never rewrite events in event stores.

Events in the store should be immutable and never change.

If an event occurs which is incorrect, then you must create a correction event which you apply to the object.

ie:

  1. account create
  2. withdrawal amend balance to -£100
  3. error correction correct balance to -£90

Reading between the lines of your question I think you are confusing the events held in the store, with the actions that occur in response to those events.

In this case we probably have a withdrawal event and a balance calc service which works out the balance. lets say the balance calc service has an error where it is not adding a the extra £10 charge to all withdrawals and you want to change the logic to fix this.

However, your problem is that you build the object state from the events in the store, so if applying that withdrawal event includes calling the service to work out the new balance then changing the implementation of that service will change the history of all your objects.

so your withdrawal event v1 comes in as

withdraw(amount = £90)

but is saved as

withdrawal
 amount = £90
 new balance = -£100

your withdrawal v2 coems in as

withdraw(amount = £90, applyCharges = false)

but is saved as

withdrawal
 amount = £90
 applyCharges = false
 new balance = -£90

when constructing the object from event store the new balance value is used rather than calling the service to calculate the new balance

0

Common wisdom is to regard the Events in an Event Store as immutable, since the Event Store also serves as a source of truth from an audit perspective, and if events have been changed, means its impossible to retrospectively determine the actual 'leaf' state of the domain at a given point in time when replaying the events.

In your example with the buggy balance in production, you won't be able to hide the mistake from say the auditors - it is likely to be a better idea to compensate for the incorrect balance, by adding a later, contra-transaction which corrects the balance.

However, we have occasionally seen fit to amend the event store - our domain isn't as financially sensitive as yours.

Resequencing of events

  • Occasionally, we receive events that arrive out of chronological order (this can be common when events are sent with non-FIFO technologies like REST and HTTP, or if there are multiple sources of events with different latency). Rather than re-order the events chronologically every time we read the event store, it was felt to be more performant to correct the sequence in the event store itself, so that the event store can again be read in a natural, stream order.

Cancellation of Events

  • The events we receive are generated by humans, and fairly frequently, mistakes are made, and the preceding event(s) are 'undone'. Again, for performance reasons, rather than apply redundant events just to have then 'undone', it was felt quicker (and easier) to (logically) tag both the original, cancelled event, and the cancellation event as 'deleted', and these would be ignored in all future folds. If we didn't do this, we would need to explicitly write 'undo' logic for every different type of event received.

Re : What about change of event store schema in newer versions - this is a difficult problem to solve. Ideally, all new versions should be backwardly compatable with older versions (and if new fields are introduced, then these should have appropriate defaults). Otherwise, seemingly you will need to write event store migration scripts to translate older versions of events to new ones.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.