CQS stands for the command query separation. There’s a chance that you may have not heard about it, but on the other hand the CQRS might ring a bell. Even though these 2 patterns have very much in common, there is a significant difference (definitely a bigger one than the additional “R” character within the CQRS acronym) in how do they apply to the architecture of our system. In this post I’ll focus on the CQS – the older brother of the CQRS – that will help you understand how to design the software that is less error prone.
At first, I do encourage you to read the formal definition of the CQS on the Martin Fowler’s blog, in case it’s really the first time you have ever heard about this term. Honestly, it’s quite easy pattern to understand, basically it all boils down to just a two principles: first one about executing an action via command that has side effects, and the second one about fetching the results via query that has no side effects. But let us not be mistaken, simple is powerful (especially here). So what is the CQS really about? Well, there are two things to remember:
- The command mutates the state, but does not return a value.
- The query returns a value, but does not mutate the state.
So what does it really mean? Let’s take a look at the example below:
void Create(string email, string name);
User GetByEmail(string email);
IList<User> FindAllByName(string name);
As we can see, the method Create() does not return any value. Its only job is to create a new user account (and probably perform some validation first etc.). The important part here is that we are certain that this action has some kind of “side effects” (new user account will be created), so it does mutate the state.
On the other hand, both the GetByEmail() and FindAllByName() functions, do not have any kind of “side effects” (do not mutate the state). These actions (queries) are responsible only for fetching the users from some data storage. It means that we can execute them N number of times, and we will always get the same results.
Having that knowledge, we can redefine the above principles into something like this:
- The command has side effects (e.g. creates and saves a new entity) and will be of type void (or Task for the async methods).
- The query has no side effects and will be idempotent (always returns the same output e.g. the list of users).
And these are the most important pieces of the CQS pattern. You don’t really have to use the command and query handlers to follow these principles. Surely, they do fit well with this pattern but are not crucial. The important lesson here is to keep in mind that if you try to design the business logic in a way that the read & write operations will be separate from each other, it will help to reduce the number of errors (some unknown behavior/side effects) and also improve the scalability of the application (you could move read & write operations into the separate services, like physically separate not just logically). Anyway, before you try the CQRS (which is a far more advanced pattern) make sure you master the CQS first.