CQRS in Azure - Part 1

What is CQRS?

Command and Query Responsibility Segregation (CQRS) is a software pattern where there is a separation between queries (read) and commands (modify).  CQRS is based on command query separation which is an approach to object oriented design and not a new concept as it first appeared in the late 80's in Bertrand Meyer's book Object-Oriented Software Construction.  The pattern is to define entities into clear responsibility of either returning results and not changing state or changing state and not returning results.

As an illustration, take a simple entity that maintains inventory:

[code lang="csharp"]
interface IInventoryService
{
InventoryModel GetInventory(int id);
IEnumerable<InventoryModel> GetInventory();
void UpdateInventory(int id, int quantity);
void AddInventory(int id, string name, int quantity);
}

Would now look like this following a CQRS approach:

[code lang="csharp"]

interface IInventoryQueryService
{
InventoryModel GetInventory(int id);
IEnumerable<InventoryModel> GetInventory();
}

interface IInventoryCommandService
{
void UpdateInventory(int id, int quantity);
void AddInventory(int id, string name, int quantity);
}

Hmm... is that it? Pretty much. But there are always subtle details to figure out!

Why CQRS?

Object Oriented Design (OOD) is an approach that allows us to design software to solve complex problems by breaking the complexity down into smaller interacting entities.  OOD has many patterns and concepts that translate well to Service Oriented Architecture (SOA) and building solutions using microservices.

CQRS is a pattern that can be applied at the object level, service and server level.

The initial IInventoryQueryService above is at the object level but the approach could be applied to how services are constructed as well as how those services are hosted.  This series of posts considers the impact at the service level primarily when the services are isolated and only share a common repository (re., the database).  This is illustrated below:

diagramsplit

There have been many posts about avoiding CQRS and this series of posts does take liberties about the definition of CQRS so it is probably a good idea to stress the point that this is not about splitting a single entity service into a command service and an update service.  Instead the pattern of CQRS here is about complex models where the view and/or the maintenance is distributed among different and potentially isolated sections of a solution.  The intention is to provide guidance and/or concepts to aide the single monolithic solution developer/designer transition to microservices.

Advantages

CQRS does fit well with some scenarios and has some compelling factors behind it:

  • Model Separation
    In modelling terms we are able to have multiple representations for our datamodel.  The clear separation allows for choosing different frameworks or techniques over others that are more suitable for query or command.  Arguably this is achievable with CRUD style entities though the single datalayer assembly often emerges.
  • Collaboration
    In some enterprises, a separation between query and command would benefit the teams involved in building complex systems particularly when some teams are more suited for different aspects of an entity.  For example, a team more concerned about presentation could concentrate on the query model while another team concerned more about data integrity could maintain the command model.
  • Independent Scalability
    Many solutions tend to either require more reads against the model or more writes depending on the business requirements.

Drawbacks

  • Staleness
    Staleness of data is always a challenge in a solution where there is the possibility of data changing after it has been presented.  The level of staleness that is acceptable is the important factor to consider (re., the amount of possible staleness the business will accept) and will greatly vary depending on the business scenario.  Consider an Azure WebApp that uses an Azure SQL database.  As soon as the data (re., the entity) is read there is the potential for the entity to become stale.  This is illustrated below where Mary reads the value of entity A as version 1.  While this is presented to Mary as version 1, Steve updates entity A which becomes version 2.

    untitled

    Click to play animation!

    With the separation of query and command, staleness is an important consideration. Looking back at our example CQRS has been applied and this is before any approach to handling the stale model state:

    Click to animate!

    Click to play animation!

  • Consistency
    Staleness of data in the presentation is always a factor but consistent reads and writes is even more of a concern when a model's state is held in multiple layers, for example an in-memory cache or a cache server like Azure Redis Cache.  One of the biggest perceived drawback of CQRS stems from the challenge of how to keep the two separate entities consistent.  Eventual consistency is a term often referred to with CQRS where over the time the collective model state becomes updated to reflect changes.  In the following illustration, the query web app is now load balanced with an in memory cache and now things get really interesting especially when the activity is interleaved and no mechanism is in place for ensuring the data model in the different layers is consistent (also known as a dog's breakfast):

    Click to animate!

    Click to animate!

  • Adoption
    Err... it is different.  Introducing new approaches into an organization can raise barriers as the approach and the benefits will need to be understood.  Benefits, such as improved collaboration, might be hard to measure so convincing stakeholders to invest in new approaches might be challenging.  Here are some some great quotes from members of the Customer Advisory Council that sums up the adoption challenge well.

When to use?

CQRS can be a good pattern to apply for task based or event driven systems especially when the solution is composed of multiple applications and not a single monolithic website or application.  It is a pattern and not an architecture so it should should be applied in specific cases and not in all business scenarios.  The last statement is assuming an organization's IT strategy is driven by business need and not IT technology or architecture (known as the follow the latest trend strategy).

Service Oriented Architecture

In Service Oriented Architecture (SOA), CQRS would be implemented within a business component and fits well in event driven scenarios where the command service benefits from being separate from the query service.  In the early days of SOA, this was often combined with having an enterprise service bus (ESB) used to notify the query service when the update service made changes to some shared model or repository.

esb

Domain-driven Design

The Bounded Context of Domain-driven design (DDD) defines the interrelationships within a complex domain.  Contexts showing clear separation between query and command would most likely be more suited than contexts where the separation does not exist.  For example, take a car dealership.  The view of a customer from the bounded context of sales would be different from the context of service though they might share some of the same models:

boundedcontext

The diagram shows that from a sales perspective, a view of customer is different as the business is more concerned about potential opportunities to sell a new car than what service has been done to their existing ones.  The temptation might be to build an over-arching view of the customer that supports both views and allows for both opportunities and vehicle history to be updated but imagine that the car dealership wants all potential opportunities visible across all locations where service related information should be limited to just a single location.  Two different views, maintenance of different information, independent scaling and isolation... this would be a good candidate for CQRS.

What's Next?

Without repeating great posts by Martin Fowler, Greg Young and Udi Dahan, this post provides background for an illustration of CQRS in Azure using Azure Functions, Azure Webs and Azure SQL Server.  For more context of CQRS, the following is recommended:

The next post, CQRS in Azure - Part 2, will take a fictitious inventory solution and apply the CQRS pattern using Azure Functions.