Implementing Domain Queries

My current Repository interface looks something like this:

public interface IRepository
    {
        T FindOne<T>(int id);
        T FindBy<T>(Expression<Func<T, bool>> expression);
        IEnumerable<T> FindAllBy<T>(Expression<Func<T, bool>> expression);
        IEnumerable<T> FindAll<T>();
        T FindOneBy<T>(Expression<Func<T, bool>> expression);
        void Save<T>(T target);
        void Update<T>(T target);
        void SaveOrUpdate<T>(T target);
        void Delete<T>(T target);
        IEnumerable<T> Query<T>(Expression<Func<T, bool>> expression);
    }

It’s modeled after the Repository class that Jeremy and Chad put in the early versions in Fluent NHibernate (now removed).  It relies almost completely on Linq expressions for where statements, which make queries very easy to write and understand.  Like this one:

_repository.FindBy<User>(user => user.Enabled == true);

This works great for small where statements, but when you need more control over the query, the current implementation doesn’t really allow you to fully take advantage of NHibernate or the Linq provider.  Another downside is your code is not really DRY when you have small where statements littered everywhere.

So to solve these problems I implemented a very simple Domain Query pattern.  Domain Queries are classes that encapsulate complicated or common queries into self contained objects that can be reused throughout your application.  I had some very simple goals for this implementation:

  1. Keep it simple to access from the executing code.
  2. I wanted full access to all of Nhibernate’s functionality when I needed it.
  3. Keep it testable.

So Let’s start with the end and then show the beginning.  I wanted it really simple to access a domain query.  This is what I had in mind for executing a domain query:

_repostiory.FindAll(Queries.GetAllActiveUsers());

To  get there I started with a really simple interface.  Because there were two basic queries I wanted to execute: return a single object or a collection of an object (I don’t have a use case for things like GetScalar yet)  I need two methods on my interface:

public interface IDomainQuery<T>
{
   T ExecuteUniqueResult(ISession session);
   IEnumerable<T> ExecuteList(ISession session);
}

I then added the following methods to my Repository:

public T FindOne<T>(IDomainQuery<T> query)
{
    return query.ExecuteUniqueResult(Session);
}

public IEnumerable<T> Query<T>(IDomainQuery<T> query)
{
    return query.ExecuteList(Session);
}

 

My repository is responsible for knowing how to access the session.  Passing the ISession object to the domain query helps me meet two of my goals, I have full access to everything on the session and it is easy to test my domain queries since they are not responsible for managing the Unit of Work.  I can create different UoW contexts in my application and my integration tests.

To create the domain queries, I use the Template pattern to abstract the IDomainQuery aspects and allow the concrete classes only deal with the query construction.  I have two abstract classes right now: LinqDomainQuery and CriteriaDomainQuery.  It’s pretty obvious what each of these do.  The CriteriaDomainQuery utilizes the DetachedCriteria functionality.  The LinqDomainQuery obviously utilizes the Linq provider.  I could easily create an HQLDomainQuery and an SQLDomainQuery as well, but following YAGNI I don’t need them yet.  Here is the LinqDomainQuery:

public abstract class LinqDomainQuery<TResult> : IDomainQuery<TResult>
{
    protected abstract IQueryable<TResult> GetQuery(ISession session);

    public TResult ExecuteUniqueResult(ISession session)
    {
        return GetQuery(session).SingleOrDefault();
    }

    public IEnumerable<TResult> ExecuteList(ISession session)
    {
        return GetQuery(session).ToList();
    }
}

Some important things to Note:  Notice that I call the SingleOrDefault() and ToList() methods.  This keeps me from having deferred execution bugs crop up.  I had some issues during testing because I was closing the session faster than I was actually executing the query.  Doing that here prevented that from happening.  Also notice that it the generic type is TResult.  With the Select statement, you can perform projections very easily. I can return DTO data directly from the query, giving me precisely the SQL statement I need and no need to map between complicated Entities to flattened DTOS.

Here is an example of  selecting a DTO from a Linq Query:

public class LoadBuilderDataQuery : LinqDomainQuery<LoadBuilderData>
{
    private readonly int _page;
    private readonly int _rows;

    public LoadBuilderDataQuery(int page, int rows)
    {
        _page = page;
        _rows = rows;
    }

    protected override IQueryable<LoadBuilderData> GetQuery(ISession session)
    {
        return
            session.Linq<OrderTicket>()
                .Skip(_page*_rows).Take(_rows)
                .Select(t => new LoadBuilderData()
                                 {
                                     LoadId = t.Id,
                                     PromisedDate = t.PromisedDate,
                                     Address = t.Destination.Address1,
                                     City = t.Destination.City,
                                     State = t.Destination.State,
                                     Zip = t.Destination.Zip,
                                     CustomerName = t.Order.Customer.CustomerName,
                                     StoreNumber = t.FulfillingStore.StoreNumber,
                                     OrderNumber = t.Order.OrderNumber,
                                     OrderSuffix = t.OrderSuffix
                                 });
           
                       
                                            
            
            
    

 

I have fully tested this query as well.  Here is an example, I’m leaving some of the setup and UoW handling out for brevity, you can get the idea.

//do some setup in the base class
public class when_retrieving_first_result_set : LoadBuilderQueryTests
{
    //unit of work helper opens and closes the session for me
    private Because of =
        () => result = UnitOfWorkHelper.Use(session => new LoadBuilderDataQuery(0, 10).ExecuteList(session));

    private It should_return_10_rows = () => result.ToArray().Length.Should().Equal(10);

    private It should_return_LoadBuilderData =
        () => result.ToArray()[0].Should().Be.OfType(typeof (LoadBuilderData));
}

  Here is the SQL Query generated:

domain_query_sql

Now back to the beginning.  To reduce some of the complexity of calling these classes, I wrapped then in static methods to make them easier to call:

public class Queries
{
    public static IDomainQuery<LoadBuilderData> GetLoadBuilderData(int page, int row)
    {
       return new LoadBuilderDataQuery([page, row);
    }
}

This is just some syntactic sugar, but small things like that keep your application easy to read and understand.

This is a very simple way to implement domain queries.   You can go a lot further with this.  Tim Scott took my simple approach and really took the training wheels off.  If I can’t get him to post about it it will.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in DDD, linq, NHibernate. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Carsten Hess

    Hello John. Very elegant solution!

    Which testframework are you using (classes “Because” and “It”) ?

    I would like to see your implementation of UnitOfWorkHelper, if you don’t mind ?

    Kind regards
    Carsten
    mail [at] carsten-hess.dk

  • Donny

    Thank you. For me, you have found the missing link(q) between the repository pattern and its crippled implementation in DDD.

    Into my next project it goes.

  • http://thomaseyde.blogspot.com/ Thomas Eyde

    Interesting design.

    Do you have one repository implementation for all types, or one for each?

  • http://thomaseyde.blogspot.com/ Thomas Eyde

    Obviously not the point of your post, but the interface in itself isn’t DRY, is it?

    What is the difference between these?

    T FindBy (Expression> expression);
    T FindOneBy (Expression> expression);

    And these?

    IEnumerable FindAllBy (Expression> expression);
    IEnumerable Query (Expression> expression);

    The names suggests they are somewhat different, but I can’t figure out what it is.

    Then there are the three saving methods:

    void Save(T target);
    void Update
    (T target);
    void SaveOrUpdate
    (T target);

    It’s probably a matter of taste, but isn’t the interface coupling itself to the inner workings of a relational database? SQL makes a difference between inserts and updates, but does that mean we should? What if I chose to implement this with an object database, wouldn’t the three methods then share the same implementation?

    And if there is an SaveOrUpdate(), why do I need the other two?

  • http://thomaseyde.blogspot.com/ Thomas Eyde

    Probably another off topic: The IDomainQuery is coupled to NHibernate via ISession, isn’t it?

  • http://developingux.com Caleb Jenkins

    Nice approach John! Do you find your self adding one off queries to the Queries class? Have you considered how you would want to better organize the Queries class? Queries.Builders.GetBuilderData() etc…

    Thanks for the great work!

  • Scott Wade

    Very interesting post and clever. Is it possible for you to post the code for your abstract CriteriaDomainQuery class just like you did with your abstract LinqDomainQuery class?

  • Blair

    What about passing in information regarding to prefetch paths?

  • http://www.lostechies.com/members/jcteague/default.aspx jcteague

    That is exactly the kind of information you encapsulate into your query object. These are not general purpose queries. These are made to be used for specific cases where the fetching strategy is specific to it’s use.

    You can pass values through the constructor. I use these for paging data. You could also pass in Expressions that become part of the linq statement as well.

  • Olly

    Generally when you page data, you also need to know the total item count.

    Is there a clean way of adding this other than adding another method to the IDomainQuery interface?

  • Jon Hilton

    Re paging, I highly recommend MVC Contrib.

    You get a convenient .AsPagination() extension method which works very well with NHibernate “under the covers”