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

PseudoCQRS: The Query Side

, 17 Mar 2014
Rate this:
Please Sign up or sign in to vote.
The second article in a series about PseudoCQRS. This article describes the Query side of PseudoCQRS.

Introduction

This article is the second in a series about PseudoCQRS, a partial implementation of the CQRS pattern first described by Greg Young. The first article, a summary of the framework and of the pattern can be found here.

The query side of CQRS is concerned with displaying the current state of the application to the user. That could be the details of a particular customer, the list of orders that customer may have made, the list of products available for purchase etc. In all these examples, the data required in order to display the view does not exist on just one entity - to display the current details of a customer, you might need data from the country table, or from the CustomerCategory table, or you might want to display a sum of the total of all the orders they've ever made. This data comes from multiple places, not just one entity.

This is the case for almost every "view" within the system. In this context, I use the word view to refer to a particular screen that users see within the application, or a report that they run, or even a model used to expose data via an API.

In the past, we (at Liquid Thinking) might have tried to design our entities so they could be reused by various views. For example, we might have added a string type Country property to our Customer entity so that we could display their country on the details page, or their country in the list of customers so we didn't have to lazy load that data via a country type property on the customer entity. This fit in somewhat with our "N-Tier" architecture - a presentation layer (at that stage, Asp.Net WebForms), a service layer and a data access layer.

That architecture then evolved for use in MVC, where we had the controller, the view and the viewmodel in the presentation tier. The service layer would get entities back from the data access layer, and would then map those into dtos which were passed up to the controller, which would then map THOSE dtos into ViewModels. A lot of mapping. And a lot of pain when something needed to be changed in the view. Which happened regularly. Even with a tool like AutoMapper, changes to a screen required changes to the view, the controller, the ViewModel, the dto and the service. And if we tried to reuse some parts of the service for multiple views, the number of things that caused changes to those sections increased.

It never felt quite right.

CQRS recognises that there are two sides to your standard data based application - the Query side, whereby users inspect the current state of their application, and the command side, where they modify the state of their application. And we've also come to recognise that the View and the ViewModel are intrinsically dependent on each other, and a ViewModel designed for one view can't (and shouldn't really if you are strongly adhering to the Single Responsibility Principle) be used with any other view (except perhaps for a mobile version of the same screen).

We wrote the PseudoCQRS framework because we like the CQRS pattern, but have an existing "N-Tier" application that we want to migrate to a more CQRS type architecture. We can't go all the way and use Event Sourcing, but we can get the advantages that CQRS provides in regards to better separating the command and the query sides of the application.

This article describes the query side of PseudoCQRS

The PseudoCQRS Architecture

The following diagram shows the objects involved in the Query side of PseudoCQRS:

When you want to write a new screen in your application, all you have to do is design the View, create the ViewModel, the ViewModelProvider and the ViewModelArguments object (and optionally, any Precheckers that must be run to prevent security violations etc). These are the things are different for every screen - the rest of the functionality is the same for every screen, and PseudoCQRS removes the need to write that code over and over again.

In order better explain the architecture, let's take a look at a concrete example.

In the PseudoCQRS repository on GitHub, there is an example project called PseudoCQRS.Examples.NerdDinner. This project shows the classic NerdDinner MVC example application done within the PseudoCQRS architecture.

This application allows users to create dinners, and to view a list of the current available dinners.

Listing the available dinners is the vertical slice of functionality that we will look at in this example. Here is the page we're going to create:

As described above, we need to create a View, a ViewModel, a ViewModelProvider and ViewModelArguments in order to create this functionality.

The ViewModel

The ViewModel is an object that contains all the data necessary to display the View. In this case, that's a list of the available dinners, plus a list of all the hosts so we can filter by host, so our ViewModel and associated objects look like this:

public class DinnerListViewModel
{
    public int HostedByUserId { get; set; }
    public Dictionary<int, string> Hosts { get; set; }
    public List<DinnerViewModel> Dinners { get; set; }
}

public class DinnerViewModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string DateTime { get; set; }
    public string HostedBy { get; set; }
} 

On our list page, we want to display a list of all the dinners with the title of the dinner, its Date, and who it is hosted by. We also need its Id in order to create links to further details pages, or to register for the dinner. And we want to be able to filter by host, so we need to display a drop down list with all the host names in. Our view model contains just exactly all the data we need to display that page.

The ViewModel Arguments

The ViewModel Arguments object is the object that contains all the data necessary to determine how to build the ViewModel. For the dinner list in our example, we can either display all the upcoming dinners, or we can filter it to only a list of dinners hosted by a particular person. So for our arguments, we just need one property, the id of the hosting user:

public class DinnerListArguments
{
    public int HostedByUserId { get; set; }
} 

The ViewModelProvider

The ViewModelProvider object is the thing that actually draws the data from the database (or file system, or RSS feed, etc). In our example, we will use our ORM to pull a list of Dinner entity objects from the database, filtered to only upcoming dinners, and if necessary, only those dinners hosted by a particular person:

public class DinnerListViewModelProvider 
    : IViewModelProvider<DinnerListViewModel, DinnerListArguments>
{
    private readonly IRepository _repository;

    public DinnerListViewModelProvider( IRepository repository )
    {
        _repository = repository;
    }

    public DinnerListViewModel GetViewModel( DinnerListArguments arguments )
    {
        return new DinnerListViewModel
        {
            Dinners = GetDinners( arguments ),
            Hosts = GetHosts(),
            HostedByUserId = arguments.HostedByUserId
        };
    }

    private Dictionary<int, string> GetHosts()
    {
        return _repository.GetAll<User>().ToDictionary( x => x.Id, x => x.Name );
    }

    private List<DinnerViewModel> GetDinners( DinnerListArguments arguments )
    {
        return _repository
            .GetAll<Dinner>()
            .Where( dinner => DinnerShouldBeIncluded( arguments, dinner ) )
            .Select( dinner => new DinnerViewModel()
            {
                Id = dinner.Id,
                Title = dinner.Title,
                DateTime = dinner.EventDate.ToString( "dd/MM/yyyy" ),
                HostedBy = dinner.HostedBy.Name
            } ).ToList();
    }

    private static bool DinnerShouldBeIncluded( 
        DinnerListArguments arguments, 
        Dinner dinner )
    {
        return 
            dinner.EventDate >= DateTime.Now.Date
            && 
            ( 
                arguments.HostedByUserId == 0 
                || 
                dinner.HostedBy.Id == arguments.HostedByUserId 
            );
    }
} 

You can see that we are allowing the list of dinners to be filtered by host. So the "DinnerShouldBeIncluded" method is used to determine whether the dinner should be included in the list. It should be included if its data is today or later AND its host is the same as the host we are filtering by OR we are not filtering at all.

In this simple example, we are using our ORM to draw all the data necessary for our ViewModel. For more complicated views, what we usually do is have a custom SQL statement that draws the data in a very efficient way, and which draws only exactly the data necessary from the database, and then use the SqlObjectHydrator package to convert a datareader into a ViewModel or into a list of ViewModel objects. This way, our query side is extremely fast. And because each View has one ViewModel, and one ViewModelProvider, you can be confident that if you modify any of that code, it's only the one view that you will be affecting.

The View

The view simply displays the data from the ViewModelProvider:

@model PseudoCQRS.Examples.NerdDinner.Modules.DinnerList.DinnerListViewModel
@{
    ViewBag.Title = "List";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>List of Dinners</h1>
<div class="well">
    <div>
        Filter by Host:
        @Html.DropDownListFor(  x => x.HostedByUserId, new SelectList( Model.Hosts, "Key", "Value"), "Select host", new { onchange = "location.href='?HostedByUserId=' + this.value"} )
    </div>

</div>
@foreach ( var dinner in Model.Dinners )
{
    <div class="panel panel-default">
        <div class="panel-body">
            <h4>@dinner.Title</h4>
            <div>Date: @dinner.DateTime</div>
            <div>Hosted By:@dinner.HostedBy</div>
        </div>
    </div>
}
<button class="btn btn-primary" onclick=" location.href = '/DinnerCreate/Execute' ">Create a dinner</button>

Again, it's an extremely simple example - it just displays a drop down list for the hosts filter, and lists out the details of each of the dinners that have been returned by our ViewModelProvider. In this case we're not even using the Id to link to a details page of the dinner, but you can see how that would easily be achieved.

The Controller

Finally, we just need to write a controller that will wire all the bits up and return the HTML to the browser:

public class DinnerListController : BaseReadController<DinnerListViewModel, DinnerListArguments>
{
    public override string ViewPath
    {
        get { return "Dinner/List"; }
    }
} 

You can see that we use one controller per action, and in this case the controller inherits from our BaseReadController object, which is a generic object requiring the type of the ViewModel and the ViewModel arguments as its generic parameters. Then all we need to do is tell it where the actual View for this screen is stored (the "ViewPath" override) and we're done!

So for each new screen in your application, the above are the only items that you need to create; a Controller, a View, a ViewModel, a ViewModelProvider and an arguments object to hold the arguments for the ViewModelProvider. Each of those objects has one responsibility, and if the screen needs to change, these are the only items that you would need to modify, and if you modify them you can be confident that you haven't affected any other features in your application. (Of course, you would want to have a suite of unit and acceptance tests in place to prove that...)

The PseudoCQRS components that use the above objects in order to display the data are as follows:

The BaseReadController

For the query side of PseudoCQRS, we provide two base controller which can automatically do all the necessary wire up. These are the BaseReadController and the BaseReadExecuteController. The BaseReadExecuteController will be discussed in a later article, but the BaseReadController is listed below:

[DbSessionManager]
public abstract class BaseReadController<TViewModel, TArgs> : Controller, IViewPath
    where TViewModel : class
    where TArgs : new()
{
    private readonly IViewModelFactory<TViewModel, TArgs> _viewModelFactory;

    public abstract string ViewPath { get; }

    protected BaseReadController( IViewModelFactory<TViewModel, TArgs> viewModelFactory )
    {
        _viewModelFactory = viewModelFactory;
    }

    public BaseReadController()
        : this( ServiceLocator.Current.GetInstance<IViewModelFactory<TViewModel, TArgs>>() ) {}

    public virtual ActionResult Execute()
    {
        return GetActionResult( _viewModelFactory.GetViewModel() );
    }

    protected virtual ViewResult GetViewResult( TViewModel viewModel )
    {
        return View( this.GetView(), viewModel );
    }

    protected virtual ActionResult GetActionResult( TViewModel viewModel )
    {
        return GetViewResult( viewModel );
    }
} 

This object is a generic base object which takes the type of a ViewModel object and the type of a ViewModelArguments object as its generic parameters. Into the controller is injected an IViewModelFactory object of the same generic types. Each controller has just one action - an "Execute" action. That action calls the ViewModelFactory to get the ViewModel and then returns the appropriate action for that view. By default, that's just to return an MVC ViewResult, using the ViewModel as its model, but the two methods GetViewResult and GetActionResult are marked as virtual so they can be overridden to return JSONResults or ContentResults etc.

The ViewModelFactory

The ViewModelFactory is responsible for getting the view model arguments object, passing that to the ViewModelProvider and returning the ViewModel to the controller. Its code listing is below:

public class ViewModelFactory<TViewModel, TArg> : IViewModelFactory<TViewModel, TArg>
    where TViewModel : class
    where TArg : new()
{
    private readonly IViewModelProvider<TViewModel, TArg> _viewModelProvider;
    private readonly IViewModelProviderArgumentsProvider _argumentsProvider;
    private readonly IPrerequisitesChecker _prerequisitesChecker;

    public ViewModelFactory(
        IViewModelProvider<TViewModel, TArg> viewModelProvider,
        IViewModelProviderArgumentsProvider argumentsProvider,
        IPrerequisitesChecker prerequisitesChecker )
    {
        _viewModelProvider = viewModelProvider;
        _argumentsProvider = argumentsProvider;
        _prerequisitesChecker = prerequisitesChecker;
    }

    public TViewModel GetViewModel()
    {
        var args = _argumentsProvider.GetArguments<TArg>();
        var checkResult = _prerequisitesChecker.Check( args );
        if ( checkResult.ContainsError )
            throw new ArgumentException( checkResult.Message );

        return _viewModelProvider.GetViewModel( args );
    }
}     

ASIDE: As with all objects within PseudoCQRS, ViewModelFactory implements an interface (in this case IViewModelFactory) that you could implement yourself if you wanted the factory to work in a different fashion. You then just need to wire up your IoC container accordingly to point to your implementation and not the PseudoCQRS implementation. All the PseudoCQRS components can be replaced in this fashion if necessary.

The ViewModelFactory gets a ViewModelProvider (in our example, that's the DinnerListViewModelProvider), an IViewModelProviderArgumentsProvider and an IPrerequisitesChecker objects injected into it. It uses these objects as follows:

1) It calls the IViewModelProviderArgumentsProvider object to get the arguments required to pass to the ViewModelProvider

2) It sends that arguments object to the IPrerequisitesChecker. That object can do things like make sure this user has access to the object specified by the arguments, or check to make sure the arguments are well formed etc.

3) If the IPrerequisitesChecker is happy that everything's ok, it then calls the GetViewModel method of the IViewModelProvider with the arguments, and returns the result.

The ViewModelProviderArgumentsProvider

The ViewModelProviderArgumentsProvider is responsible for constructing the arguments object that is sent to the ViewModelProvider:

public class ViewModelProviderArgumentsProvider : IViewModelProviderArgumentsProvider
{
    private readonly IPropertyValueProviderFactory _propertyValueProviderFactory;

    public ViewModelProviderArgumentsProvider( IPropertyValueProviderFactory propertyValueProviderFactory )
    {
        _propertyValueProviderFactory = propertyValueProviderFactory;
    }

    private readonly PropertyInfoCacher _propertyInfoCacher = new PropertyInfoCacher();

    public TArg GetArguments<TArg>() where TArg : new()
    {
        var retVal = new TArg();
        var properties = _propertyInfoCacher.GetProperties( typeof ( TArg ) );
        var propertyValueProviders = _propertyValueProviderFactory.GetPropertyValueProviders();
        foreach ( var propertyValueProvider in propertyValueProviders )
        {
            foreach ( var kvp in properties )
            {
                if ( propertyValueProvider.HasValue<TArg>( kvp.Key ) )
                    kvp.Value.SetValue( retVal, propertyValueProvider.GetValue<TArg>( kvp.Key, kvp.Value.PropertyType ), null );
            }
        }
        return retVal;
    }

    public void Persist<TArg>() where TArg : new()
    {
        Persist( GetArguments<TArg>() );
    }

    internal void Persist<TArg>( TArg arguments )
    {
        foreach ( var property in typeof ( TArg ).GetProperties().Where( x => Attribute.IsDefined( x, typeof ( PersistAttribute ) ) ) )
        {
            var persistLocation = ( Attribute.GetCustomAttribute( property, typeof ( PersistAttribute ) ) as PersistAttribute ).PersistanceLocation;
            var propertyValueProvider = _propertyValueProviderFactory.GetPersistablePropertyValueProvider( persistLocation );
            var propertyValue = property.GetValue( arguments, null );
            if ( propertyValue != null )
                propertyValueProvider.SetValue<TArg>( property.Name, propertyValue );
        }
    }
} 

This object works in pretty much the same way that the standard MVC ModelBinders work - it looks in the Querystring, the Form, the RouteData, the Session and the Cookie objects for entries matching the names of the properties on the object that it is constructing, and if it finds any such items, it sets the property on the object accordingly.

The GetArguments method works as follows:

1) It creates a new object of type TArgs. In our example, that means it creates a new DinnerListArguments object.

2) It calls a PropertyInfoCacher object to get a list of all the property set methods on that object. The PropertyInfoCacher uses reflection to get this list of set property methods, and once it has looked them up for a particular type, it caches them so they can be retrieved more quickly the next time.

3) It calls the IPropertyValueProviderFactory to get a list of PropertyValueProvider objects to use in order to try to set the properties on the arguments object. The implementation of IPropertyValueProviderFactory in PseudoCQRS returns PropertyValueProviders that look in the following places for property values (and in the following order):

  • Cookie
  • Session
  • RouteData
  • QueryString
  • Form

4) It loops through each of these PropertyValueProviders asking them if the can provide a value for each of the properties on the object, and if they can, it sets the value on that property accordingly.

5) Finally, it returns the arguments object.

For our example, the DinnerListArguments contains just one property, the HostedByUserId property. So the ViewModelProviderArgumentsProvider first checks to see if there is an entry in the Cookie object named "HostedByUserId", and if there is one, it tries to set the value of the property to the Cookie's value (if it can be converted to an integer. If it can't it just ignores it). The it checks in Session for an entry named "HostedByUserId". Then RouteData. Then the QueryString, and finally, the Form. In all cases, if it finds an entry, it sets the value accordingly, so a Form entry will override a QueryString entry, which will override a RouteData entry etc etc etc.

In our example, when you change the "Filter by Host" drop down list, it causes a get request with a HostedByUserId entry in the querystring. So when the ViewModelProviderArgumentsProvider returns the DinnerListArguments object, the value of "HostedByUserId" will be the value that it finds in the querystring.

One other thing to note about the ViewModelProviderArgumentsProvider object is that it has a Persist method on it too. This method can be used to persist any values in your arguments object that you want to store in a persistable format - ie, to Session or to Cookies (or to whatever other persistance mechanism you want if you write your own PropertyValueProvider objects). This method will look for any properties on your arguments object that are decorated with the "Persist" attribute, and for those properties, call the SetValue method on the appropriate PropertyValueProvider which should implement the persistance mechanism. This feature is useful in cases where you want to persist the state of the screen somehow so when you come back to the screen, the same data is displayed (for example, the same page of data on a paged list).

That's basically it! Once the arguments object has been created, it is sent to the ViewModelProvider that we wrote, and that object returns a ViewModel to the ViewModelFactory, which in turn returns it to the Controller, which in turn selects the View to use to display the data and it is returned to the caller accordingly.

Conclusion

PseudoCQRS takes care of all the boilerplate code that you might otherwise have to write when creating query screens for your application. The only things you need to write are the things that are specific to that actual screen (the View, the ViewModel, the ViewModelProvider etc).

In a later article I will describe the command side of the architecture where the user can actually make modifications to the state of their data.

The code for the framework can be found at the PseudoCQRS repository on GitHub

You can also install PseudoCQRS via NuGet (MVC4 and MVC5 versions are also available).

SOME NOTES:

I have not gone in to complete detail on all the objects that are involved in the query process. For example, you will note that the BaseReadController is decorated with a "DbSessionManager" attribute which actually deals with opening and closing the connection to our database so the ORM queries work. Similarly, I haven't fully explained how the PrerequisitesChecker works, or how all the objects get injected into the objects that rely on them. These topics are beyond the scope of this article, but in the example application on the GitHub repository, you can follow the code through in a debugger to see exactly what everything is doing, or read up on NHibernate sessions, Inversion of Control containers, AOP concepts with regards to attributes etc to find out more.

We have also created a PseudoCQRS Bootstrap project which will do all the bootstraping necessary to get a project working with PseudoCQRS and the following technologies: Unity, AutoMapper, FluentValidation and NHibernate. Again, these items are outside the scope of this article, but if you have any questions, please feel free to ask in the comments section.

License

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

About the Author

Matthew Davies
Founder Liquid Thinking Ltd
United Kingdom United Kingdom
Founder of Liquid Thinking Ltd, supplier of software to league, venue and event managers since 2002

Comments and Discussions

 
GeneralMy vote of 5 PinpremiumPrasad Khandekar8-Jun-14 20:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 17 Mar 2014
Article Copyright 2014 by Matthew Davies
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid