Click here to Skip to main content
15,886,025 members
Articles / Hosted Services / Azure

CQRS on Windows Azure - The Query Side

Rate me:
Please Sign up or sign in to vote.
4.53/5 (3 votes)
14 Apr 2014CPOL3 min read 11.8K   6  
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.

VB.NET
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:

VB.NET
''' <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

Depending on your business scenario, you might also like to add properties that describe the query context - when it was submitted, the address and credentials used to submit it and any priority to be applied to it.  This can be done by adding a wrapper interface around the query definition so that the context is still kept distinct from the parameters required by the query itself.

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 could be resolved using Unity, for example.

VB.NET
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 definition
    ''' </returns>
    Function Handle(ByVal query As IQueryDefinition) As TResult

End Interface

The job of the query handler is to validate the correctness of the query context and then to use the query parameters to perform the data retrieval from the underlying data source and finally to coerce it into the return type expected by the query definition.

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 its 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:

VB.NET
    <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

  • 14th April, 2014: Initial version

License

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


Written By
Software Developer
Ireland Ireland
C# / SQL Server developer
Microsoft MVP (Azure) 2017
Microsoft MVP (Visual Basic) 2006, 2007

Comments and Discussions

 
-- There are no messages in this forum --