Command and Query Responsibility Separation, or CQRS, has been a term popping up quite often recently. It is definitely not the solution to everything, but it certainly makes some kind of solutions better. There are some frameworks to make the implementation easier, such as NCQRS and it certainly is very interesting, but it’s still very much in infancy.
If you would go with the full CQRS + Event Sourcing solution, depending on the experience and skill level of the developers the project might end up being much more costly than expected, even if CQRS + Event Sourcing offers superior architecture in many cases and can be applied to many problems, it doesn’t mean it’s always the correct answer.
I recently did an implementation of a CQRS-inspired solution, without using the full blown route using event sourcing. The requirements were as follows:
- All the queries need to be filtered according to user’s privilege level.
- All the commands need to pass through an authorization layer.
- All changes to system need to be logged, at the command level.
- It should be possible to expose all the functionality of the system through simple APIs.
Applying the CQRS principle in this case makes it easier write Query and Command Handlers that are able to deal with these kind of requirements. There are two sets of objects, Commands and Queries, each with corresponding handlers. QueryHandlers work synchronously and respond to Query objects. CommandHandlers as per CQRS do not return any results, and can work either synchronously or asynchronously, and process Command objects. QueryHandlers are not allowed commit any transactions (though this is not enforced by the code at this point). QueryHandlers work with a filtering layer that filter any results returned. CommandHandlers work with an authorization layer that authorizes every command in terms of a user context. Commands succeed as a whole, or fail as a whole. Both types of handlers work against the same data store. The query and command layers are the only possible interaction with the underlying data store, the only notable exception being the “authentication” layer. Performance isn’t an issue at this point so there hasn’t been any need to go with a different “Read Model” which offers a denormalized view.
When exposing APIs, it is quite easy to expose all the Query and Commands in a straightforward way and have all the logic for filtering and authorization still intact.
Of course all of this can be achieved by simpling turning everything into Requests and writing Requests Handlers or doing standard CRUD, but having the seperation makes it possible to make all commands function asynchronously or add some kind of caching layer for the queries later on.