to CRUD or not to CRUD and Client side vs. Server side UUIDs

I’m coming from a REST + monolithic background (not sure if that is an asset or liability) and am struggling to wrap my head around some fundamental concepts in Axon and CQRS.

Many of the examples I find online, such as: Axon Trader, Event Store Client Example, and PCF Axon CQRS Demo are heavy on create events but slim on update and delete events. I’m not grasping how to include updates and deletes in a DDD/CQRS friendly way (any examples out there?) In this thread, Allard states somewhat passionately that: “CRUD events are the source of all evil…”. What does the alternative look like?

Newb question: Is an aggregate identifier that was used for a create event, also used for updates and deletes on the same aggregate or is it just used to identify the atomic transaction in the event store?

Some musings on UUIDs that relate to the above: It seems advantageous to generate them on the client side. The examples above do this. The aggregate UUIDs flow through to the query side where they are used as primary keys on entities. Is this standard practice? From the reading I’ve done, doing so will cause problems for read side databases because randomly generated UUIDs result in frequent page splits because primary keys are no longer sequential. Sequential, time-based UUIDs generated on the server side can prevent this fragmentation but then it becomes difficult to report the new query side entity id back to the command side REST call that created the aggregate (if trying to implement HATEOS links). How do other folks manage UUIDs between the command-side event store and query-side database(s)?


Hi Justin,

all knowledge is an asset :wink:

The reason I stated that “CRUD events are […] evil”, is because they don’t capture intent. Instead, they focus on what an end-state should look like. Axon stimulates its users to think about the actual actions you want to perform. Of course, sometimes these actions are simply “update this data field”. But if that’s all you have, you might start wondering if CQRS is a good choice.
So instead of “updating” a balance on a bank account, you “withdraw”, or “deposit” money. In this case, the intent is much more clear than when “update the balance to its current amount -10”.

The UUID’s generated identify an aggregate, for example, a Bank Account. All actions that target that bank account, must carry that identifier. An “transaction” is identifier using correlationID’s in the event meta data and basically refer back to the ID of the command message itself.

The reason UUIDs are used, it because they are safe to generate on the client side. The fact that they may create some page splits in a query model is an issue that must be addressed exactly there: in the query model. Also note that bigger applications don’t have “the query model”; they have several query models for different purposes. Each stored in a database and using a (database) model that is suitable for the purpose they serve. There is nothing from with using a separate (otherwise worthless) sequential primary key to query model entries if it helps your performance. Simply add the UUID as a secondary key, if even required.

The whole reason for CQRS is (or at least should be), that you can optimize both “sides” of the application for their own purpose.

Hope this clarifies things.