We have an existing CRUD-based system, but are hitting a number of pain points keeping the data organised for all the various views that are increasingly required of it. I'm assessing whether splitting the read and write models (i.e. CQRS) might provide some benefit here.
The system handles the product catalogue for an online store. Aside from adding and removing products to/from the catalogue, it is also possible to update an existing product's entry in the catalogue. Such updates could happen for a number of reasons, including:
- to correct erroneous info;
- to improve marketing presentation (new product names, descriptions, images, categorisation);
- to effect new pricing; and
- to effect changes to taxation.
Such updates are not currently distinguished by the existing CRUD system, but the intent behind them is of interest to other systems in our domain (e.g. marketing would like analyses of how each type of update correlates with sales data; legal would like to track what erroneous information has been corrected and when) although for the product catalogue system itself that intent is not very interesting.
My first thought is therefore that if we move from our CRUD system, we could introduce a task-based UI in which updates to the catalogue would be distinguished by different user actions—and these would in turn each give rise to a corresponding CQRS command:
CorrectErroneousInfo
ImproveMarketingPresentation
EffectNewPricing
EffectChangesToTaxation
But what events are we interested in? Coming from the existing CRUD system, I find it hard to escape thinking that the product catalogue system will want something like:
ProductNameChanged
ProductDescriptionChanged
ProductAddedToCategory
ProductRemovedFromCategory
ProductPriceIncreased
ProductPriceDecreased
But these events do not convey the original intent behind each update: they appear to be very "CRUD-like" in that they merely state that particular properties were updated without stating why (the first two are the worst offenders, but they all have this nature to some extent). Furthermore, a single command could clearly trigger multiple events—and the marketing team would like to maintain knowledge of which events originated from a single action. Should the events carry fields that relate them to the originating command? Coupling events to commands in this way feels wrong.
An obvious alternative would be just to raise one event for each command, for example:
ErroneousInfoCorrected
MarketingPresentationImproved
NewPricingEffected
ChangesToTaxationEffected
But then understanding how these "log-like" events have modified the domain model is much more complicated: one will essentially have to rerun the command, albeit infallibly. Whilst on some level this is akin to event sourcing, it feels like the tail is wagging the dog: our domain events are being determined by our commands, rather than by the facts our system needs to record.
A third option would be to do both: raise both "CRUD-like" events for the product catalogue, and also "log-like" events for those who wish to perform such analyses. But would this not be duplicating the same information across multiple events? I generally like to minimise duplication, but is it acceptable here? Is the logging of (successful) commands as domain events (in addition to more specific events to which those commands gave rise) a common pattern?
Is there some insight I'm lacking here? Should I be approaching this differently altogether? Or are one (or all) of the approaches outlined above actually reasonable?