My #CQRS cookbook
Here is my personal take on how I interpret all things CQRS...
Commercial impact: why use CQRS ?
CQRS enforces you to apply strict boundaries to your app in a consistent way.
These boundaries convert your single application into 3 separate parts, only coupled by commands, events and queries.
By using CQRS you convert your single app into 3 apps who can be built in parallel.
So basically, when you do have enough resources, the approach can decrease your time to implement something by a factor of 2.5 (estimate, 3 times - 0.5 for the common interfaces - commands, events and queries).
Since all the parts require a different kind of development knowledge, it is easier to assign parts to developers based on their expertise/domain knowlege etc.
It also implies that your app is composed of smaller - and thus more maintainable- units.
CQRS also enables distributed applications as well (i.e. higly scalable cloud apps)
For a software shop, CQRS can be a huge commercial advantage...
(On a technical side note CQRS also makes integration testing super simple, due to it's boundaries)
Explaining CQRS and MVC: the simplest explanation possible (AFAIK)
(For reasons of simplicity, sagas are not mentioned here)
This is how MVC works with CQRS
- UI invokes a function on the controller
- A controller function can either:
- Query a viewmodel and send it to the UI
- Run a command using the following sequence
- Build a command
- Push the command to the bus and verify whether it was a valid command
- Redirect to another controller function (which is chosen based on the validity of the command)
This is how the bus works
- The bus receives and validates a command before accepting it
- The bus delegates a valid command to a command handler
- The command handler converts the valid command in to a function call on an aggregate root
- The function call on the aggregate root can send events (state changes in the domain)
- The aggregate roots consume the published events (i.e. changes are applied to the respective domain entities)
- The event handlers consume the published events (i.e. changes are applied to the respective viewmodels)
A Command represents a request to change the domain state.
A Command is consumed once by a Command Handler.
Command names should always be in the imperative (i.e. CancelOrder )
An event represents a state change in the domain.
Events are handled by AR's, Sagas and Event Handlers
Event names should always be in the past tense (i.e. OrderCanceled )
Aggregate root (AR)
DDD definition +
An AR contains public functions which can publish Events. (these events happen in a single transactional boundary - think unit of work)
An AR can also consume Events which are about itself.
A saga consolidates Event information accross AR boundaries.
A saga can raise Commands based on the consolidated events or a time event.
A saga is considered a temporal object (i.e. limited lifetime)
A command handler converts a command into a function call on an AR by:
- Converting the command's parameters into the target function's parameters, sometimes using the viewmodel to convert and/or extend the command parameters into parameters understandable by the AR.
- Calling the function on the AR.
- The function will emit zero or more events
An event handler applies the state changes to it's respective target (i.e. a viewmodel or an AR)
A view model is a representation of the UI data using a first normal form datamodel
How to handle business logic which crosses multiple AR boundaries ?
If you need to process some logic which crosses multiple AR boundaries, you can either create a new AR which references those AR's, or create a Saga which consolidates events about these ARs.
To know which one to create, you need to know your domain expert's opinion on the transition process. If the domain expert only cares about the endresult, you should use a saga. If your domain expert needs to be exposed to the transformation process in itself, then you should use an AR.