Search code examples
domain-driven-designevent-sourcingeventstoredb

Is there an accepted way of dealing with events containing ID of another aggregate?


Lets say an Invoice was generated for a Customer. This specific event would look something like this:

invoice.raised {
   "id": "4dbcff82-6f35-4155-9aec-f8185c1f932f",
   "total": "50.00",
   "description": "Order 01133",
   "customer_id": "c2206843-414d-454f-9894-57c6b11b9c00"
}

This is fairly simple example and it refers to customer aggregate that an invoice belongs to.

Difficulty manifests itself when I want to create Invoices view and I want to embed Customer's name in an invoice. I could do two things - enrich the original event to contain Customer's name or load up the Customers view to find out what the name is when I am building my Invoices view. Now this is not a very complicated example, but in some cases it becomes almost unmanageable to enrich events anymore as I end up with a lot of properties of various aggregates copied into the event that is very specific to some other aggregate.

Is there a universally accepted way of dealing with this apart from enriching events? Because everytime now the invoice.cancelled event is raised I will have to also include the amount of the invoice once again, so that I can update Customers view with new balance for instance.


Solution

  • in some cases it becomes almost unmanageable to enrich events anymore as I end up with a lot of properties of various aggregates copied into the event that is very specific to some other aggregate.

    That's right, it makes your events less manageable.

    So I'd say that it's enough to have just customer_id and ask for customer name when you build your read model:

    • It can be at projecting time, when denormalised view is built.
    • It can be at querying time, when resulted view model is built based on invoices and customers (for this use case you can have dedicated list of customers with customer_id and customer_name only)

    Because everytime now the invoice.cancelled event is raised I will have to also include the amount of the invoice once again, so that I can update Customers view with new balance for instance.

    invoice.cancelled event is triggered by Invoice aggregate root, I expect, and it owns amount so it is natural to put amount into invoice.cancelled event.

    invoice.raised event is triggered by Invoice aggregate root too but it does not own customer name. It cannot check if customer name is consistent at specific point in time anyway. That's why instead of embedding customer name you can simly query it when you build your read model.

    BTW there is a good reading about designing events - 6 Code Smells with your CQRS Events – and How to Avoid Them