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.
iamkoch
May 17, 2012
Looks a bit like the Specification pattern:
http://devlicio.us/blogs/jeff_perrin/archive/2006/12/13/the-specification-pattern.aspx
intoxicat
May 17, 2012
Hi – Yes there are some similarities although the specification pattern allows you to do chaining to combine specifications logically where the Query Object patterns primary concern is separation of concerns.
(ps. I think I have managed to turn moderation off now) 🙂
dlorenz54115
May 13, 2017
I would question the need to need to have specific Db parts on the method. Wouldn’t you just ioc inject that into the constructor? How the query gets its data should be of no concern to the caller. It just wants the data. The only thing the method might need is the filter object if any.