Command-Query separation is being popularized today with architects like Greg Young
and Udi Dahan as a way of architecting systems. It can be hard to grasp as a concept, even if we understand command-query seperation as a design pattern that influences the methods on our classes. It can be difficult to see how the principle scales to the level of architectures.
Pat Helland’s article Data on the Outside vs. Data on the Inside remains key for me in trying to understand this model. Pat distinguishes between operators and operands. Operators are commands, things we can do like order a meal, or order a book. Operands are the data that we use for that command. So order a Thai Green Curry, or orderDan Bropwn’s The Lost Symbol. This implies that we have to publish those operands so that people can discover them to use in the command. A way to think of this list of operands is as catalogue data.
I find that the XP idea of an architectural metaphor helps me to
understand the seperation between operator and operand when architecting a solution.
If I want to order a take-away from the Thai restaurant I need a menu that tells me what they deliver and how much it costs. The menu is not the food itself, it’s just an number, a name, and a price I can use to order the food with. Catalogue data has some interesting characteristics such as often being cacheable because it changes infrequently, or being versioned if we can deal with different varieties of it. But the key to this discussion is to appreciate that in order to carry out the task of ordering a takeway I need first to have a menu that lets us communicate about what I can order. In patterns terms I need to query for the food menu, and then issue the command to order the food.
Now in software we have the same need to present a user with catalogue data so that they can carry out a task. We have to tell them what operators are valid for our operands. This is where the query side of command-query comes in when thinking about command-query architectures. The query builds the catalogue – it grabs the list of acceptable operands for our operators.
This is often easy to comprehend in the context of MVC architecures. When I get a request for a page that allows you to perform a task, I want to populate it with my operands. For example, if I want to enable the task of ordering food I need to
give you a menu. I do not want to load my food or customer entities for
this, because all I need is the name for you to display in a pick list
(and possibly an id). When you perform the task, ordering the food, then I may need to
load the customer or food entities from their repository, create an order entity etc.
Now the nature of the operands creates some interesting opportunities architecturally. At the simplest leve, instead of using repositories we might just use queries. I think this is some of the answer in the repositories vs. queries debate – it also explains why FindAll on a Repository is often seen as a smell, because we
are using to obtain a collection of catalogue data and that is not a repository concern. We might use a different strategy altogether to make these queries such as NHibernate report queries or even ADO.NET (In a previous project we created a generic DataRader for reading objects with an ID and Name, called a SmartReference that we passed a stored procedure to load our catalogue data and then loaded into picklists). The objects you deal with here will often be anaemic, exactly the kind you fear in your domain model. But that is fine here, you are not creating domain objects but the operands for the operations you will perform on the domain. Some will indeed be catalogue data for items within your domain, and you load the full object in response to the command.
We might even query against a different relational store to obtain our catalogue data. This may simply be to reduce load, but it might also be because our catalogue data might be published to this store from another application. In that case the catalogue data might well not be consistent with the current state of the publisher, if there is a delay, so our catalogue data might even be versioned.
The important point is to realize that an application may have a healthy mix between domain data and catalogue data, and should handle each appropriately.