The Query Object Pattern.

Posted on May 2, 2012

4


Intro

It is not uncommon to see large, complicated queries unceremoniously plonked in repositories and even in controllers, services, whatever you choose to call your favourite abstraction. Even when concentrated inside repositories these can at times be hard to test, find and maintain. In this blog I aim to make another push for a pattern appears somewhat underused but that offers great benefits and describes the appropriate place for the common query in the codebase.

What is wrong with putting all your queries in your Repositories?

Take a look at the following interface.

public interface ICustomerRepository
{
    IEnumarable<Customer> GetCustomersWithMoreThan10RecentOrders();
    IEnumarable<Customer> GetCustomersWithUnpaidDebts();
}

The queries are nicely named but it is quite clear that the requirements that caused these queries to be created were quite different. One query (GetCustomersWithMoreThan10RecentOrders) could have been related to a story about customer care, possibly for identifying customers that should be sent special offers and the other (GetCustomersWithUnpaidDebts) was probably a feature request from the accounts department to be used for chasing unpaid debt. Still they both return a set of customers and were duly inserted into the same Customer repository. Of course it could be argued that the domain is poorly thought out and that the debt chasing probably should reference some kind of Debtor entity instead of Customer, but that is a different topic, the point is that by having these two queries in the same class the Single Responsibility Principle (SRP) is violated as the circumstances that would require the CustomerRepository class to change are not the same for the two queries.

Queries

Important queries typically encapsulate important business concepts. If they are important they probably deserve their very own .cs file, don’t you think?. It would be rude otherwise. This is pretty much all the Query Object Pattern is about. Encapsulation of the query into an object of it’s own, that has it’s own change lifetime. Udi Dahan blogged about it in 2007 and Ayende blogged about it in inimitable style in 2009. So as with most things software this is nothing new. Neither of Udi nor Ayende addressed the issue of being able to mock the query result. Ayende also leaks the NH specific implementation into the rest of the code. Personally I don’t see this as an issue but the query object pattern doesn’t need to impose a limit on the return types at all. It is more about applying decent OO principles to the subject of queries.

The Query Object Pattern.

This is my take on the Query Object Pattern and is something I have been using for a while to good effect. In short, you simply create a new class for each complicated query. This allows you to optionally delegate the execution of the query to another object, similarly to the GoF Command pattern. The Query Object Pattern is pretty much the query equivalent of the Command Pattern. Having a single class for a query naturally maintains SRP but also allow you to organise and integration test your queries easily and independently.

In my example I am dealing with NHibernate queries using ISession. Natuarally you should encapsulate any important query types, Http api calls, file system queries, anything important.

First create a simple query interface, I have chosen IQuery but have also used more specific names such as INHibernateQuery in the past. IQuery has a single method: Execute that takes an ISession as a dependency. You may need other dependencies to be passed in but for this example just an ISession will do.

public interface IQuery
{
    public TResult GetResult(ISession session);
}

The type responsible for actually calling the query is shown below: A single method interface again that takes an IQuery. Again this could be made more specific for particular query types. It could also be implemented by your UnitOfWork class, if it makes sense for you to do so, typically if your UnitOfWork manages the ISession transaction and lifetime.

public interface IQueryRunner
{
    TResult GetQueryResult(IQuery query);
}

The concrete implementation could vary but here is an approximation that could also measure and log the query performance:

public class QueryRunner
{
    public TResult GetQueryResult(IQuery query)
    {
        var session = ContainerOfChoice.Resolve();
        // start timer
        var result = query.GetResult(session);
        // stop timer - log
        return result; // result could be lazy if the query provider supports it.
    }
}

So the implementation of the Customer Repository method: CustomersWithMoreThan10RecentOrders becomes something like this when turned into a query object:

public class CustomersWithMoreThan10RecentOrders : IQuery<IEnumerable<Customer>>
{
    public IEnumerable<Customer> GetResult(ISession session)
    {
        // unpleasant NHibernate criteria stuff goes here
        return criteria.List<Customer>();
    }
}

Delegating query calls to an IQueryRunner implementation allows you to do stuff like measuring query performance and logging in a single place as well. Naturally there is nothing in the code that stops you from running the query directly as long as you can obtain a reference to ISession.

Typically you would have am IQuery interface for each type of query you need to do (Nhibernate, Http api, file system, whatever), typically depending on what dependencies you need injected to execute the query.

Mocking the query result.

As the call the Execute is delegated off to an abstraction query objects can be mocked easily by mocking the IQueryRunner based on the type passed into the GetQueryResult method. Instead of having an abstraction for each repository you now only have two, IQuery and IQueryRunner which may also help reducing your api surface area.

Arguably you could say the IQueryRunner is only there to allow testing of the code paths where the query is used but the cross cutting concerns and dependency management add additional benefits as well.

Is this CQRS?

No, well, yes, in a way. It’s hard to say if it is CQS or CQRS, either way it separates your queries for the rest of your code and make them ‘things’ of their own which is a good thing. Let’s not worry too much about labels.

Conclusion

The Query Object Pattern is tidy, allows for easy refactoring and testing and obeys the Single Responsibility Principle and is a good pattern to consider introducing into pretty much any codebase that works on data.

There is no point going over the top with this pattern and smother it in gold paint. Small queries I would still just run inline (can I mock it?). When they get ugly, stick them in a query object.

One question that may be raised is: What is the role of the repository? Well you can LoadById and Update. No need for entity specific repositories any more in my view. Perhaps there is no need for repositories at all if you use an ORM that already provides a suitable abstraction. Food for thought.

Tally ho.

Advertisements
Tagged: , , ,
Posted in: Patterns