Friday 6 May 2016

Not all events are equal

The of the more interesting realisations to make as you travel the CQRS path is that not all event data is equal. One of the simplest examples to help demonstrate this is applying event data to an aggregate.


Paint this picture if you will.

You have an aggregate, lets call it a Product aggregate. When you create a Product the business has said we have to provide a name and a category. As developers we duly capture these two requirements as properties on a CreateProduct command.


The command gets passed into our aggregate, maybe directly or indirectly via a command handler, but that's not important, what is important is that the name and category arrives in the Product aggregate Create method.


Maybe we do basic data validation like checking if the name and category properties are provided (we'll avoid implementation details here as there's a long list of ways to validate what is appropriate and what isn't and where and when to do it). We then raise an event saying the Product has been created with a ProductCreated event.


Eventually this might arrive in an event handler to update several read store for you projections and read models/views, but before that your aggregate will handle the event itself to apply the state within the event to itself. This is where things can get interesting.


As it stands the business hasn't said there's any other business rules, so here's where we start to see that not all event data is irrelevant.


Take the name property. This data has no meaning to the domain (if you are doing domain driven design) or to the business for that matter. The name is just a human readable, short description of the product. It means absolutely nothing to the businesses computer systems. It's view data only, something to return when viewing a projection or view of the Product aggregate. 
And here's where the penny should hopefully drop for you (replace penny with what ever currency you use).


Your aggregate doesn't actually have to remember the value of the name property internally. It doesn't need to read it off the event or apply it to itself. it can ignore it completely. Even if we have a RenameProduct command and ProductRenamed event, we still wouldn't need to remember their values.


The category property, for now, is exactly the same. there are no business rules based off it, so it can also be completely ignored. Remember the event log/event store will keep all the events so you can see later on what was recorded, plus you can always look at your read store for it's current value.

Changes to the business rules are fine - don't fear the refactor

If, several months later the business brings an enhancement to you that states the business now needs an email to be sent IF the category changes. We now have a business rule that is based on the category of the Product.

If you chose to model this by way of introducing a ChangeCategory command and CategoryChanged event, each with a NewCategory property, the same rule as above would still apply. Because you can tell directly from the command that a the category is to be changed, there's no need to retain internally the value of the category. Life goes on and things are simple... your Product aggregate is still extremely light-weight, fast and nimble.

Several more months go on and now the business has decided that the email needs to include what the product was originally categorised as.

Now things get a little more interesting. Here you can take one of two paths.

If you chose to make changes to the original the ChangeCategory command and CategoryChanged event to include an additional property called OriginalCategory then again, the same rules from above apply. All the required information you need for your existing business rules are there without the aggregate having to maintain state on the category.

If, however, you choose not to add a new property called OriginalCategory to the ChangeCategory command, and only to the CategoryChanged event, then you have an issue... how do you populate the new field, as it's not going to be provided by the client application. This is when you now need to start recording the value of Category internally so that you can read it's current value, populate the OriginalCategory property on the CategoryChanged event so the email can be generated with the newly required information.

I'm not going to suggest one way over the other. There are advantages and disadvantages to both, but the lesson to take away from this is that, as stated at the beginning "not all event data is equal".