Click here to Skip to main content
Click here to Skip to main content

Tagged as

CQRS on Windows Azure - The query side

, 14 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
The query side of the Command Query segregation or responsibility architecture

Introduction

This article shows a way of making a RESTful interface to implement the query side of a Command Query Responsibility Segregation architecture. It is implemented in VB.Net but there is nothing in it preventing a very quick conversion to C# if that is your language of choice.

Background

At its simplest CQRS is an architecture pattern that separates commands (doing) from queries (asking). What this means in practice is that the command and query side do not need to share a common model and this vertical shearing layer is often matched with a vertical shearing layer between the definition (of a command or query) and the implementation (in a command handler or a query handler).

In my experience, CQRS is very useful when you are developing an application with a distributed team to a very short timescale as it reduces the risk of "model contention" which can occur if everyone is updating the model at the same time. It is also useful for financial scenarios where having an audit trail of all the changes that have occurred in an application is required.

The challenges

There are a couple of challenges I wanted to address on the query side that affected how the architecture went together:

Minimise the payload

Passing state information back and forth and indeed any system that maintains state is going to result in an impediment to scaling. In practice this means that every query must have all the information required to fulfil the request in the query definition alone.

Independent development

A shearing layer is needed between the front end (in this case the web service) and the actual implementation of the query logic. This allows the how part to change without impacting the what part.

Query definitions and query handlers

The architecture is therefore split into two components - the (rigid) query definition which states what is being requested and the related query handler which performs the work of servicing the query

Query Definition

All query definitions inherit from an interface - IQueryDefinition - which uniquely identifies the query type, specific instance and any parameters it will require.

Public Interface IQueryDefinition

    ''' <summary>
    ''' The unique key by which this query instance can be identified
    ''' </summary>
    ''' <remarks>
    ''' This allows queries to be persisted and for the query handler to record whether or not it has
    ''' executed a given query.  This should not be assumed to be sequential.
    ''' </remarks>
    ReadOnly Property InstanceIdentifier As Guid

    ''' <summary>
    ''' The human-readable name of the query
    ''' </summary>
    ''' <remarks>
    ''' For a high-frequency or low data use scenario an enumerated type can be used but for most cases a readable text
    ''' name for the query is preferable.
    ''' </remarks>
    ReadOnly Property QueryName As String

    ''' <summary>
    ''' Add a parameter to the list of parameters for this query
    ''' </summary>
    ''' <param name="name">
    ''' The name of the parameter to add
    ''' </param>
    ''' <param name="index">
    ''' The index of the parameter (0 if not applicable)
    ''' </param>
    ''' <param name="value">
    ''' The value to use for the parameter
    ''' </param>
    Sub AddParameter(ByVal name As String, ByVal index As Integer, ByVal value As Object)

    ''' <summary>
    ''' The set of parameters for this query
    ''' </summary>
    ''' <remarks>
    ''' This may be null to indicate that a query has no parameters
    ''' </remarks>
    ReadOnly Property Parameters As IEnumerable(Of QueryParameter)

End Interface

This interface is extended so it also defines what the expected return type for the query is to be

''' <summary>
''' Interface for any class that is a definition of a query to be passed to the query handler
''' </summary>
''' <remarks>
''' Because the query handler uses IoC, each query must have its own distinct definition class.
''' This interface and the QueryDefinitionBase class allow for common functionality to be enforced
''' across these classes.
''' </remarks>
Public Interface IQueryDefinition(Of TResult)
    Inherits IQueryDefinition

End Interface

Query Handler

The query handler takes the query definition and returns the required TResult. There is a one to one mapping between definition classes and handler classes, which is resolved using Unity.

Public Interface IQueryHandler(Of Out TResult)

    ''' <summary>
    ''' Execute the query and return the results to the calling application
    ''' </summary>
    ''' <param name="query">
    ''' The query (and any parameters) to execute
    ''' </param>
    ''' <returns>
    ''' The result set as specified by the query handler deifinition
    ''' </returns>
    Function Handle(ByVal query As IQueryDefinition) As TResult

End Interface

Routing the URI to the appropriate query definition

All the queries come in on the same root URL so the routing to the correct one is done by the URI template (which, therefore, has to uniquely identify the query and it's parameters)

This is done by having each query definition having a static function that returns its URI template and using these in the WCF service definition

    <OperationContract()>
    <WebGet(ResponseFormat:=WebMessageFormat.Json,
            RequestFormat:=WebMessageFormat.Json,
            BodyStyle:=WebMessageBodyStyle.Wrapped,
            UriTemplate:=GetBazaarsForClientQueryDefinition.UriTemplate)>
    Function GetBazaarsForClient(ByVal clientId As String, ByVal query As String) As IReadOnlyList(Of BazaarSummary)

History

Keep a running update of any changes or improvements you've made here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Duncan Edwards Jones
Software Developer (Senior)
Ireland Ireland
C# / SQL Server developer
Microsoft MVP 2006, 2007
Visual Basic .NET
Follow on   Twitter   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150129.1 | Last Updated 14 Apr 2014
Article Copyright 2014 by Duncan Edwards Jones
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid