Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CQRS events do not contain details needed for updating read model

Tags:

cqrs

There is one thing about CQRS I do not get: How to update the read model when the raised event does not contain the details needed for updating the read model.

Unfortunately, this is a quite common scenario.

Example: I add a user to a group, so I send a addUserToGroup(userId, groupId) command. This is received, handled by the command handler, the userAddedToGroup event is created, stored and published.

Now, an event handler receives this event and the both IDs. Now there shall be a view that lists all users with the names of the groups they're in. To update the read model for that view, we do need the user id (which we have) and the group name (which we don't have, we only have its id).

So the question is: How do I handle this scenario?

Currently, four options come to my mind, all with their specific disadvantages:

  1. The read model asks the domain. => Forbidden, and not even possible, as the domain only has behavior, no (public) state.

  2. The read model reads the group name from another table in the read model. => Works, but what if there is no matching table?

  3. Add the neccessary data to the event. => Does not work, as this means that I had to update all previous events as well, and I cannot foresee which data I may need one day.

  4. Do not handle the event via a "usual" event handler, but start an ETL process in the background that deals with the event store, creates the neccessary data and writes the read model. => Works, but to me this seems a little bit of way too much overhead for such a simple scenario.

So, the question is: How do I deal with this scenario correctly?

like image 951
Golo Roden Avatar asked Sep 09 '12 15:09

Golo Roden


2 Answers

There are two common solutions.

1) "Event Enrichment" is where you indeed put information on the event that reflects the information you are mentioning, e.g. the group name. Doing this is somewhere between modeling your domain properly and cheating. If you know, for instance, that group names change, emitting the name at the moment of the change is not a bad idea. Imagine when you create a line item on a quote or invoice, you want to emit the price of the good sold on the invoice created event. This is because you must honor that price, even if it changes later.

2) Project several streams at once. Write a projector which watches information from the various streams and joins them together. You might watch user and group events as well as your user added to group event. Depending on the ordering of events in your system, you may know that a user is in a group before you know the name of the group, but you should know the general properties of your event store before you get going.

like image 175
Sebastian Good Avatar answered Sep 22 '22 14:09

Sebastian Good


Events don't necessarily represent a one-to-one mapping of the commands that have initiated the process in the first place. For instance, if you have a command:

SubmitPurchaseOrder
    Shopping Cart Id
    Shipping Address
    Billing Address

The resulting event might look like the following:

PurchaseOrderSubmitted
    Items (Id, Name, Amount, Price)
    Shipping Address
    Shipping Provider
    Our Shipping Cost
    Shipping Cost billed to Customer
    Billing Address
    VAT %
    VAT Amount
    First Time Customer
    ...

Usually the information is available to the domain model (either by being provided by the command or as being known internal state of the concerned aggregate or by being calculated as part of processing.)

Additionally the event can be enriched by querying the read model or even a different BC (e.g. to retrieve the actual VAT % depending on state) during processing.

You're correctly assuming that events can (and probably will) change over time. This basically doesn't matter at all if you employ versioning: Add the new event (e.g. SubmitPurchaseOrderV2) and add an appropriate event handler to all the classes that are supposed to consume it. No need to change the old event, it can still be consumed since you don't modify the interface, you extend it. This basically comes down to a very good example of the Open/Closed Principle in practice.

like image 20
Dennis Traub Avatar answered Sep 20 '22 14:09

Dennis Traub