I’d summarise Command Query Separation as:
- divide methods into queries and commands
- only mutate state in commands
- never mutate state in queries
- make it easy to tell which is which
All of which I agree with. The part I think is silly is:
- command methods should always return void
The argument is that this makes it easy to identify which methods have side effects. The downside is that if you want to get some information on how your command fared, you have to make a second call. That’s not an issue in itself. The issue is that the object has to keep the information about the last command in case you want it. You’ve taken an operation that you think should be atomic, and in order to honour CQS you’ve made it into two coupled operations. This is the worst form of coupling, because it’s hidden.
There are ways to restore atomicity to our newly dual operation. You can make the command and the status request transactional in some way via locking or a transaction manager. This implies that the status request is either undefined or unreliable outside of the transaction. Things are getting worse, not better.
In practice, everybody gives themselves an out. Tim Curry and Martin Fowler write in support of CQS yet both convince themselves that returning a value isn’t that bad as long as you do it for the right reasons. As do many others.
Let’s look at what Betrand Meyer himself said: “Asking a question should not change the answer”.
That’s a pretty succinct argument against writing query methods with side effects. It doesn’t have much to say about writing command methods with return values. Yes, always returning void from command methods makes it easy to see which methods have side effects. But there are other ways to do that (naming conventions anyone?) which avoid the serious problems that the “always return void” rule creates.
In conclusion, I’d replace that last rule with these ones:
- use a naming convention to identify command methods
- the return value of a command method should only contain information about the command, not about the system state
It might be claimed that the difficulties introduced by strict adherence to CQS are a symptom of deep problems with OO as a paradigm, not of any issue with CQS as a principle. I’m sympathetic to that view; functional and reactive programming both address the objections raised above, and do so at a paradigm level. However, this article is aimed at developers sitting squarely in an OO paradigm and wondering if they should feel guilty for returning a status code from a command function.