The command-query separation principle has been around for a while but recently I’ve come across a few situations where CQS can be useful outside of programming for which it was originally intended for. It’s interesting to discover that principles devised for a specific context can be applied successfully to other areas such as application design and SOA services. In this first post I will explain what CQS means in it’s traditional programming context.
CQS and coding
Wikipedia has the following definition of CQS.
every method should either be a command that performs an action, or a query that returns data to the caller, but not both
Ok, that tells you to separate code that "changes" things from code that just reads "things". The main reason for this is that code that violates CQS is very hard to understand just by looking at it. Lets try to explain this with a simple example.
If a customer has ordered products with a total amount above a certain threshold during the last year he should be upgraded to gold status.
One way solving this would be:
if (customer.PlaceOrder(order, LastYear) > RatingThreshold.Gold)
This code violates CQS in by having the PlaceOrder method return the total order value accumulated since the date specified by the second parameter. Violating CQS always lead to code that is harder understand just by reading it. A quick look at the code above will not give answer to questions like.
- What effect does the parameter LastYear have on place order?
- Is customers upgraded if a single order is above the threshold?
Given the fact that developers spend most of their time browsing source code makes all efforts to write code that is clean and easy to understand extremely valuable.
So let’s see how can we refactor the code above to avoid violating CQS:
var totalOrderValue = customer.TotalOrderValueSince(LastYear);
if (totalOrderValue > RatingThreshold.Gold)
If you look at the code above you will see that it’s much easier to understand just by reading in (no need to examine the internals of PlaceOrder). We achieved this by separating the query part into a separate method, TotalOrderValueSince. By doing this we can now answer the questions above.
- The order value that is used is clearly the accumulated value for the last year.
- Customers is obviously only upgraded if the current order makes the accumulated value exceed the threshold for gold customer status.
So the conclusion is that enforcing CQS in an programming context gives you cleaner and more readable code. In my next post we will take a closer look on how CQS can be applied to improve application design.