0

I have been asking a lot of questions on event sourcing so apologies on that but I would like to nail this right from the get go.

SETUP

| p_key | invoice_id | EmployeeId | Event type        | Version | Data |
|-------|------------|------------|-------------------|---------|------|
| 1     | 12345      | E456       | Invoice_Generated | 1       | JSON |
| 2     | 12345      | E567       | Invoice_Reviewed  | 2       | JSON |
| 3     | 12345      | E456       | Invoice_Paid      | 3       | JSON |
| 4     | 12345      | E142       | Invoice_Comment   | 4       | JSON |
| 5     | 12345      | E412       | Invoice_Comment   | 5       | JSON |
| 6     | 12346      | E999       | Invoice_Paid      | 7       | JSON |
| 7     | 12345      | E456       | Invoice_Refunded  | 8       | JSON |

I am assuming the invoiceId is the aggregate . Since the version numbers will increment for every change made to invoice.

Use Case:

The event store contains all events applied on invoice and also contains info on which employee applied it. In the current scenario an invoice is generated,reviewed,paid. Someone noticed some issue made a few comments and then we decide to post a new payment before we refunded the old one[the event that is being undone is earlier in history].

API CALL:

refund/invoice/{invoiceid}/{employeeid}

FLOW OF THINGS

Option 1

  • Retrieve all events for a given invoice
  • Save all events in event stream with the latest version saved.
  • Loop through all events to find the required instance of event.
  • Apply an "UNDO" action on that event and give it version number of (latest+1)
  • make a call to database to check the latest version in event store. Validate it is same as that saved in Event Stream.
  • Ensure that the new event is larger than last version in stream. We assume that is it also a higher version that "DO ACTION".

ISSUES

  • Retrieving and saving all events in application every time can start to get expensive.
  • Seems like a hell lot of work for just "UNDO" action.

OPTION 2:

  • I make 2 calls to database
    1. Call to get event based on invoice Id and employeeId
    2. Call to get latest version of last event applied on invoice.
  • Undo changes based on data in event
  • Create an "UNDO" event with version no 1 greater than latest.
  • make another call to database to get the latest version again.
  • ensure the "UNDO" version is exactly 1 greater than latest version

ISSUES

Not sure if this is right. Should we have things added to event stream multiple times.

Also is it okay to query event database twice once with only invoice id and once with invoice id and employee id

Please let me know if I am missing something or if my version style is wrong or if my assumption of aggregate is wrong.

Praveen
  • 557
  • 1
  • 5
  • 20

1 Answers1

2

I'd suggest getting a few points clear in your mind.

You probably want to use the same plumbing no matter what changes you are making to the domain model; whether processing a new command, or undoing an earlier one, the actual mechanics should be treated the same way.

If "Undo" is something you need, then that should be something your domain model understands how to do, and the shape of the data you keep in memory should support the capabilities. So if you are designing a model for invoice that allows you to query specific events in its history, your in memory representation should be giving you access to those events.

The first rule of performance optimization is "don't", but if you are worried about the costs of reading data from the database, you can cache hot representations of your models.

If you are trying to figure out optimistic writes in a relational database, Jonathan Oliver is a good starting point.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • I do not get what you mean by same plumbing ? Are you inferring that my aggregate needs to be different (i.e not invoice Id but invoice+employeeId) always? – Praveen Apr 25 '18 at 18:31
  • On a different not am I thinking about UNDO wrong? Is UNDO not the process of retrieving events from event store and reversing it ? – Praveen Apr 25 '18 at 18:39
  • @Praveen: Maybe your understanding of the aggregate is wrong. Aggregate isn't a field, it's an object/domain model, which has the responsibility and all changes done, are done through the aggregate. An aggregate also identifiers your transaction boundary, so everything with the aggregate is saved and loaded together. https://martinfowler.com/bliki/DDD_Aggregate.html – Tseng Apr 30 '18 at 14:50
  • @Praveen what I guess VoiceOfUnreason is suggesting is that "Undo/Refund" is just another domain operation with a business intent and meaning. "Applying an undo action on an event" feels like out-of-band technical stuff. Instead, `Refund` should really go through the same execution route as a regular command like `Review` or `Pay`. No need to tinker with the event stream. – guillaume31 May 02 '18 at 08:31
  • @guillaume31 I would not expect the api to send me a Undo action with the amounts right. I would want to retrieve the Payment action get the value and undo it. Imagine a scenario where use sends post payment of $100 and later then post refund of $500 . I would end up in an inconsistent state in that case wont I? – Praveen May 02 '18 at 19:26
  • That's not what I'm suggesting. Can you clarify exactly what you mean by *Apply an "UNDO" action on that event* in the `Refund` use case? Is it a technical action where you manipulate the stream? Do you remove the event from the stream? Somehow calculate the inverse effect and apply it? What is "undo"? It's really not clear in the Q. – guillaume31 May 03 '18 at 07:16