Command Query Separation (CQS)

Introduction

CQS was a term that was coined by Bertrand Meyer and presented itself in his book Object Oriented Software Construction (I haven’t read the book, yet!) the principle states that every method should either be a command that changes state or a query in which case it does not change any state but not both.

In all I agree with the principle I can think of some valid examples that are valid to break it but on the whole it’s makes sense to follow it, however what I want to show in this post is to take this principle and show how it can be applied at various levels rather than just at the method level.

Architectural Level

If we imagine a system which manages lots of amounts of data and the business would like to perform analysis on this data while still providing transactional throughput it would not be uncommon to split the data into separate Online Transaction Processing (OLTP) and Online Analytical Processing (OLAP), this  is a good example of separating the data that will be changing often (OLTP) from the stagnant data that can be analysed (OLAP) I feel this matches strongly to the principle, the benefits gained typically result in better performance and data that reflects the needs of the operations applied to it.

Object Design Level

When we have a complex domain that require numerous behaviours we want to take advantage of domain modelling in our objects by utilising something like DDD to come up with entities, value objects etc… that we can use to represent the domain were working in.

We use these objects to perform transactional operations such as “Submitting an Order” or “Creating a New User” as these represent where we are going to using the behaviours and rules we put together using our objects. What we don’t want to start doing is then using these same objects for displaying result grids on a search screen, this represents a very different usage pattern and if we try to use our objects for this purpose and also for transactional processing we get into a mess quite quickly.

Instead it’s best to take onboard the CQS principle and separate our domain model objects that will be used for out transaction processing from what I think of as reporting or querying objects that are only interested in grabbing data for things such as search screens & reports. So if we were in a issue tracking domain we would probably end up with something like this:

 
// uses ORM such as nHibernate concerned only with using objects in a transactional 
// context does not do any reporting or querying 
public class IssueRepository : IIssueRepository 
{ 
	public Issue FindBy(int id) 
	{ 
		// use ORM to get by id 
	} 
	
	public Issue Save(Issue issue) 
	{ 
		// use ORM to save only used when new as object will be tracked 
		// for updates automatically 
	} 
		
	public void Delete(Issue issue) 
	{ 
		// use ORM to delete 
	} 
} 

// probably just uses ADO.NET raw or a light facade on top of ADO.NET 
// may use nHibernate but DOES NOT return any transactional objects only 
// projections 
public class IssueQuerying : IIssueQuerying 
{ 
	public DataTable FindMatchingCriteria(int priorityId, int pageNo, int pageCount, out int virtualCount) 
	{ 
		// use ADO.NET to fill datatable of results, possibly using SP to support paging 
		// for older DB 
	} 

	public IssueDisplayModel FindAssignedTo(string username) 
	{ 
		// use nHibernate to fill projection object IssueDisplayModel 
	} 
} 

The benefits we get from separating these 2 concerns:

  1. Our domain model objects are not polluted with lots of getters just to be used for reporting/querying
  2. We can utilise databinding because our querying object returns objects that ASP.NET controls know what to do with (one way databinding only)
  3. Our mapping is simplified as we only need to map our domain model objects to other objects for transactional processing needs not querying/reporting.

Summary

Hopefully this post has shown how we can take the original CQS principle and see how it applies to other levels when developing software from the high level architecture right down to the method level.

Advertisements