Sunday, November 11, 2012

CQRS Query Engine - Writing a Testable, maintainable and extensible CQRS Query Engine - Part 3

  1. Designing the Query Engine
  2. Encapsulating common behavior (template pattern)
  3. Implementing Queries
  4. Creating the Query Resolver

Finally in this chapter I will show you the result of the Query Engine and in the next post I will show a way to decouple the query handler from your code.


So let’s start creating queries.


Querying By ID


The first Query I want to create is this one, just a simple query by ID


A table/view can have a primary key composed of just one field or by several fields, with this approach you can express the intent of the query explicitly and specify any number of ID’s to query your objects. Furthermore, sometimes your tables/views could have unique indexes additional to the primary key, you can use this approach to also find each unique items


So let’s see the code:


We need an object implementing the IQueryID mark interface to specify it as the ID we want to search for.


public sealed class MovieID : IQueryID
{
    public MovieID(Guid id)
    {
        this.ID = id;
    }

    public Guid ID { get; private set; }
}

That’s it, we are explicitly specifying the ID used by our Movie entity.


What we’re actually doing is: we are taking an implicit hidden domain concept (the Movie Identifier) and we are making it explicit


The next step is to create the query:


public class FindMovieByIDQueryHandler : QueryByIDHandler<MovieID, Movie>
{
    private IMoviesQueryRepository moviesQueryRepository;

    public FindMovieByIDQueryHandler(IMoviesQueryRepository moviesQueryRepository)
    {
        this.moviesQueryRepository = moviesQueryRepository;
    }

    protected override IQueryable<Movie> InitialItems
    {
        get { return this.moviesQueryRepository.GetItems(); }
    }

    protected override Movie FindItem(MovieID queryID, IQueryable<Movie> items)
    {
        return items.FirstOrDefault(x => x.ID.Equals(queryID.ID));
    }
}

That’s it. You created your first query completely isolated so that it can be safely unit tested. And since this query is isolated, creating new queries won’t interfere in any way with any other queries created, so there’s no risk to break existing queries =), you won’t require to change any interfaces nor existing implementations in order to add a new query, you could even create this query in a new assembly, drop it to your application’s bin folder and use reflection to load it into your IoC container when the application starts.


But we are missing something, first let me remember you the definition of the QueryByIDHandler class:


public abstract class QueryByIDHandler<TQueryID, TQueryResult> : IQueryByIDHandler<TQueryID, TQueryResult>
    where TQueryID : IQueryID

The class implements IQueryByIDHandler<TQueryID, TQueryResult>, so with this code, you would have to inject this dependency (interface) in the object you want to use your query, so let’s imagine that you want to use it in a MVC application, your code would look like:


Note: I know that you would never use a handler in this way, I’m just using it as an example. And I want to emphasize, the following code is not meant to be used in this way because you would be tight coupling your code with this specific Query Handler


public partial class MoviesController : Controller
{
    private IQueryByIDHandler<MovieID, Movie> queryByIdHandler;

    public MoviesController(IQueryByIDHandler<MovieID, Movie> queryByIdHandler)
    {
        this.queryByIdHandler = queryByIdHandler;
    }
}

Please pay special attention to:


IQueryByIDHandler<MovieID, Movie> queryByIdHandler

This doesn’t look right to me. The nature of the IQueryByIDHandler is to be generic so that we can use it to create custom queries, but using it like this we are hiding the intention of the query.


So what’s the intention? Well in this case the intention is to simply provide a way to query the Movies collection to find a specific Movie by ID


To me the correct naming would be something like:


IFindMovieByIDQueryHandler

In this case we are making the concept and the intention of the query explicit in the application. The readability of the code is dramatically increased when you read:


IFindMovieByIDQueryHandler

vs


IQueryByIDHandler<MovieID, Movie>

In order to create this interface we need code like this:


public interface IFindMovieByIDQueryHandler : IQueryByIDHandler<MovieID, Movie>
{
}

And then use it to mark our query:


public class FindMovieByIDQueryHandler : QueryByIDHandler<MovieID, Movie>, IFindMovieByIDQueryHandler
{
    private IMoviesQueryRepository moviesQueryRepository;

    public FindMovieByIDQueryHandler(IMoviesQueryRepository moviesQueryRepository)
    {
        this.moviesQueryRepository = moviesQueryRepository;
    }

    protected override IQueryable<Movie> InitialItems
    {
        get { return this.moviesQueryRepository.GetItems(); }
    }

    protected override Movie FindItem(MovieID queryID, IQueryable<Movie> items)
    {
        return items.FirstOrDefault(x => x.ID.Equals(queryID.ID));
    }
}

And finally the updated controller would look like (Again just for demonstration purpose):


public partial class MoviesController : Controller
{
    private IFindMovieByIDQueryHandler findMovieByIDHandler;

    public MoviesController(IFindMovieByIDQueryHandler findMovieByIDHandler)
    {
        this.findMovieByIDHandler = findMovieByIDHandler;
    }
}

If you want to learn more about Making Roles Explicit, please see this Uddi Dahan video


Querying all items in the collection


In order to query all items (the typical FindAll method) we will create the interface to expose the query concept


public interface IFindAllMoviesQueryHandler : IQueryHandler<Movie>
{
}

Then the implementation of the query


public class FindAllMoviesQueryHandler : QueryHandler<Movie>, IFindAllMoviesQueryHandler
{
    private IMoviesQueryRepository moviesQueryRepository;

    public FindAllMoviesQueryHandler(IMoviesQueryRepository moviesQueryRepository)
    {
        this.moviesQueryRepository = moviesQueryRepository;
    }

    protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
    {
        return items.OrderByDescending(x => x.ID);
    }

    protected override IQueryable<Movie> InitialItems
    {
        get { return this.moviesQueryRepository.GetItems(); }
    }
}

The code is actually straightforward, so it doesn’t require explanation


Applying specific queries


This is the most interesting part of the Query Engine, and also the most used in RL applications.


Here we will define all the specific queries needed in the application but only when they are required, and like I explained before, adding new queries won’t affect in any way to any existing query


So let’s start by making the query concept explicit, in order to do it, we need an object implementing the IQuery interface, something like this:


public sealed class FindMoviesByTitleQuery : IQuery
{
    public FindMoviesByTitleQuery(string title)
    {
        this.Title = title;
    }

    public string Title { get; private set; }
}

In this query class you will define all fields needed in order to apply the query, in this case I just need to query by title


Then, let’s create the mark of the query handler related to this query:


public interface IFindMoviesByTitleQueryHandler : IQueryHandler<FindMoviesByTitleQuery, Movie>
{
}

And finally the implementation:


public class FindMoviesByTitleQueryHandler : QueryHandler<FindMoviesByTitleQuery, Movie>, IFindMoviesByTitleQueryHandler
{
    private IMoviesQueryRepository moviesQueryRepository;

    public FindMoviesByTitleQueryHandler(IMoviesQueryRepository moviesQueryRepository)
    {
        this.moviesQueryRepository = moviesQueryRepository;
    }

    protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
    {
        return items.OrderBy(x => x.Title);
    }

    protected override IQueryable<Movie> InitialItems
    {
        get { return this.moviesQueryRepository.GetItems(); }
    }

    protected override IQueryable<Movie> ApplyQuery(FindMoviesByTitleQuery query, IQueryable<Movie> items)
    {
        var customQuery = items;

        if (!string.IsNullOrWhiteSpace(query.Title))
        {
            customQuery = customQuery.Where(x => x.Title.ToLower().Contains(query.Title.ToLower()));
        }

        return customQuery;
    }
}

Again, the code is straightforward and self explanatory therefore it doesn’t require any explanation


At this point, you would be able to use the query handlers in your application by exposing them as dependencies of your objects, using DI, and by adding them to your IoC container, however, like I said before, if you use them this way, you would be tight coupling your handlers with your code


As an exercise for the reader, go ahead and do it, create a simple application and use the handlers directly by injecting them in your objects, just to play with them


In the next post I will show you a way to completely decouple the handlers from your code.

CQRS Query Engine - Writing a Testable, maintainable and extensible CQRS Query Engine - Part 2

  1. Designing the Query Engine
  2. Encapsulating common behavior (template pattern)
  3. Implementing Queries
  4. Creating the Query Resolver

In the previous post I created the design of the Query Engine, in this post I will create the initial implementation


I created several abstract classes to encapsulate common behavior:


public abstract  class QueryPageAndSortingBase<TQueryResult>
{
    protected abstract IQueryable<TQueryResult> ApplyDefaultOrder(IQueryable<TQueryResult> items);

    protected virtual IQueryable<TQueryResult> ApplyPagingAndSorting(IQueryable<TQueryResult> items, PagingAndSortingInfo pagingAndSortingInfo)
    {
        Condition.Requires(pagingAndSortingInfo).IsNotNull();
        Condition.Requires(items).IsNotNull();

        var customQuery = items;
        var page = pagingAndSortingInfo.Page;
        var pageIndex = page - 1;
        var pagesize = pagingAndSortingInfo.PageSize;
        var orderDirection = pagingAndSortingInfo.OrderDirection;
        var orderField = pagingAndSortingInfo.OrderByField;

        if (!string.IsNullOrWhiteSpace(orderField))
        {
            switch (orderDirection)
            {
                case OrderDirection.Ascending:
                    customQuery = customQuery.OrderBy(orderField);
                    break;
                case OrderDirection.Descending:
                    customQuery = customQuery.OrderBy(string.Format("{0} descending", orderField));
                    break;
                default:
                    throw new ArgumentOutOfRangeException("pagingAndSortingInfo", "Sorting can only be Ascending or Descending.");
            }
        }
        else
        {
            customQuery = this.ApplyDefaultOrder(customQuery);
        }

        customQuery = customQuery.Skip(pageIndex * pagesize).Take(pagesize);
        Condition.Ensures(customQuery).IsNotNull();

        return customQuery;
    }
}

The QueryPageAndSortingBase<TQueryResult> class is used to encapsulate paging and sorting behavior


public abstract class QueryHandlerBase<TQueryResult> : QueryPageAndSortingBase<TQueryResult>
{
    protected abstract IQueryable<TQueryResult> InitialItems { get; }

    protected virtual IQueryable<TQueryResult> HandleCustomQuery(IQueryable<TQueryResult> items, PagingAndSortingInfo pagingAndSortingInfo = null)
    {
        Condition.Requires(items).IsNotNull();

        var customQuery = items;

        if (pagingAndSortingInfo != null)
        {
            customQuery = this.ApplyPagingAndSorting(items: customQuery, pagingAndSortingInfo: pagingAndSortingInfo);
        }

        Condition.Ensures(customQuery).IsNotNull();

        return customQuery;
    }
}

The QueryHandlerBase<TQueryResult> controls the flow to call the QueryPageAndSortingBase<TQueryResult>.ApplyPagingAndSorting method. This method is in charge to apply the paging and sorting.


public abstract class QueryHandler<TQuery, TQueryResult> : QueryHandlerBase<TQueryResult>,
    IQueryHandler<TQuery, TQueryResult>
    where TQuery : IQuery
{
    public virtual QueryResults<TQueryResult> HandleQuery(TQuery query, PagingAndSortingInfo pagingAndSortingInfo = null)
    {
        Condition.Requires(query).Evaluate(query != null);
        Condition.Requires(this.InitialItems).IsNotNull();

        var queryProcessed = this.ApplyQuery(query, this.InitialItems);
        Condition.Ensures(queryProcessed).IsNotNull();

        var res = this.HandleCustomQuery(queryProcessed, pagingAndSortingInfo);
        Condition.Ensures(res).IsNotNull();

        return QueryResults.Of(res.ToList(), queryProcessed.Count());
    }

    protected abstract IQueryable<TQueryResult> ApplyQuery(TQuery query, IQueryable<TQueryResult> items);
}

The QueryHandler<TQuery, TQueryResult> controls the flow to apply a custom and explicit query, and apply paging and sorting when required.


public abstract class QueryHandler<TQueryResult> : QueryHandlerBase<TQueryResult>,
    IQueryHandler<TQueryResult>
{
    public virtual QueryResults<TQueryResult> HandleQuery(PagingAndSortingInfo pagingAndSortingInfo = null)
    {
        Condition.Requires(this.InitialItems).IsNotNull();

        var initialItems = this.InitialItems;
        var res = this.HandleCustomQuery(initialItems, pagingAndSortingInfo);
        Condition.Ensures(res).IsNotNull();

        return QueryResults.Of(res.ToList(), initialItems.Count());
    }
}

The QueryHandler<TQueryResult> class is used to control the flow to query all the elements applying paging and sorting when required


Unit testing


I’ll show you the unit tests of these base classes, since they are abstract, and most of the methods I want to test are protected I can’t use Moq =((((( buaaa, meaning that I have to write some testing mocks manually..... but anyway, let’s do it:


QueryPageAndSortingBase Tests


Manual Mock


public class QueryPageAndSortingBaseMock : QueryPageAndSortingBase<Movie>
{
    protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
    {
        return items.OrderByDescending(x => x.Title);
    }
}

Tests


[TestClass]
public class QueryPageAndSortingBaseTests
{
    [TestClass]
    public class TheApplyPagingAndSortingMethod
    {
        [TestMethod]
        public void it_should_apply_the_default_order_when_the_order_field_is_null_or_empty()
        {
            var sut = new QueryPageAndSortingBaseMock();
            var items = Builder<Movie>.CreateListOfSize(10).Build().AsQueryable();
            var expectedItems = items.OrderByDescending(x => x.Title);
            var methodInfo = sut.GetType().GetMethod("ApplyPagingAndSorting", BindingFlags.Instance | BindingFlags.NonPublic);
            var pagingInfo = new PagingAndSortingInfo(orderByField: string.Empty);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, pagingInfo });

            res.Should().NotBeNull()
                .And.HaveCount(items.Count())
                .And.ContainInOrder(expectedItems);
        }

        [TestMethod]
        public void it_should_order_in_ascending_mode_when_the_order_fiels_is_not_null_and_the_order_direction_is_Ascending()
        {
            var sut = new QueryPageAndSortingBaseMock();
            var items = Builder<Movie>.CreateListOfSize(10).Build().AsQueryable();
            var expectedItems = items.OrderBy(x => x.ID);
            var methodInfo = sut.GetType().GetMethod("ApplyPagingAndSorting", BindingFlags.Instance | BindingFlags.NonPublic);
            var pagingInfo = new PagingAndSortingInfo(orderByField: "ID", orderDirection: OrderDirection.Ascending);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, pagingInfo });

            res.Should().NotBeNull()
                .And.HaveCount(items.Count())
                .And.ContainInOrder(expectedItems);
        }

        [TestMethod]
        public void it_should_order_in_descending_mode_when_the_order_fiels_is_not_null_and_the_order_direction_is_Descending()
        {
            var sut = new QueryPageAndSortingBaseMock();
            var items = Builder<Movie>.CreateListOfSize(10).Build().AsQueryable();
            var expectedItems = items.OrderByDescending(x => x.ID);
            var methodInfo = sut.GetType().GetMethod("ApplyPagingAndSorting", BindingFlags.Instance | BindingFlags.NonPublic);
            var pagingInfo = new PagingAndSortingInfo(orderByField: "ID", orderDirection: OrderDirection.Descending);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, pagingInfo });

            res.Should().NotBeNull()
                .And.HaveCount(items.Count())
                .And.ContainInOrder(expectedItems);
        }

        [TestMethod]
        public void it_should_paginate_the_results()
        {
            var sut = new QueryPageAndSortingBaseMock();
            var page = 3;
            var pageSize = 4;
            var pageIndex = page - 1;
            var pagingInfo = new PagingAndSortingInfo(orderByField: "Title", orderDirection: OrderDirection.Descending, page: page, pageSize: pageSize);
            var items = Builder<Movie>.CreateListOfSize(20).Build().AsQueryable();
            var expectedItems = items.OrderByDescending(x => x.Title).Skip(pageIndex * pageSize).Take(pageSize);
            var methodInfo = sut.GetType().GetMethod("ApplyPagingAndSorting", BindingFlags.Instance | BindingFlags.NonPublic);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, pagingInfo });

            res.Should().NotBeNull()
                .And.HaveCount(pageSize)
                .And.ContainInOrder(expectedItems);
        }
    }
}

QueryHandlerBase Tests


Manual Mocks


public class QueryHandlerBaseMock : QueryHandlerBase<Movie>
{
    protected override IQueryable<Movie> InitialItems
    {
        get { throw new NotImplementedException(); }
    }

    protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
    {
        throw new NotImplementedException();
    }
}

Tests


[TestClass]
public class QueryHandlerBaseTests
{
    [TestClass]
    public class TheHandleCustomQueryMethod
    {
        [TestMethod]
        public void it_should_not_apply_paging_nor_sorting_when_the_pagingAndSortingInfo_parameter_is_null()
        {
            var sut = new QueryHandlerBaseMock();
            var items = Builder<Movie>.CreateListOfSize(20).Build().AsQueryable();
            var paging = (PagingAndSortingInfo)null;
            var methodInfo = sut.GetType().GetMethod("HandleCustomQuery", BindingFlags.NonPublic | BindingFlags.Instance);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, paging });

            res.Should().NotBeNull()
                .And.HaveCount(items.Count())
                .And.ContainInOrder(items);
        }

        [TestMethod]
        public void it_should_apply_paging_and_sorting_when_the_pagingAndSortingInfo_parameter_is_not_null()
        {
            var sut = new QueryHandlerBaseMock();
            var items = Builder<Movie>.CreateListOfSize(20).Build().AsQueryable();
            var paging = new PagingAndSortingInfo(orderByField: "ID");
            var pageIndex = paging.Page - 1;
            var expectedItems = items.OrderBy(x => x.ID).Skip(pageIndex * paging.PageSize).Take(paging.PageSize);
            var methodInfo = sut.GetType().GetMethod("HandleCustomQuery", BindingFlags.NonPublic | BindingFlags.Instance);

            var res = (IQueryable<Movie>)methodInfo.Invoke(sut, new object[] { items, paging });

            res.Should().NotBeNull()
                .And.HaveCount(expectedItems.Count())
                .And.ContainInOrder(expectedItems);
        }
    }
}

Uff that was too much code for a post, the rest of the tests will be available when I post the full code =)


In the next post, I will show you how to use these objects and how easy is to create query objects


This is the graphical representation of these classes:




Saturday, November 10, 2012

CQRS Query Engine - Writing a Testable, maintainable and extensible CQRS Query Engine

  1. Designing the Query Engine
  2. Encapsulating common behavior (template pattern)
  3. Implementing Queries
  4. Creating the Query Resolver

In my last post I blogged about writing a Query Repository in a test-friendly way but even when the code was testable I still believe it can be improved in several ways.


I’m going to build a small, simple and test-friendly Query Engine. This will be the first post in this series.


If you are familiar with CQRS you will find the code of this post familiar, since I’m following the same patterns used in the Commands branch in a CQRS architecture


The traditional query repository


Before start, let’s see how a typical query repository looks like (assuming a fictitious Movies query repository):


public interface IMoviesQueryRepository
{
    Movie FindByID(Guid movieID);
    IQueryable<Movie> FindAll();
    IQueryable<Movie> FindByTitle(string title);
    IQueryable<Movie> FindByDirectors(string[] director);
    IQueryable<Movie> FindByActors(string[] actors);
    IQueryable<Movie> FindByReleaseDate(DateTime date);
}

There are several problems with an interface like this, I will talk about them in a minute.


The problem


Now let’s say that you start with a design like this and that you implement all these methods and use DI to inject the query repository wherever you want to use it. Now imagine that after awhile, you find out that you need to query your movies by Category so you just go and add the following method to your interface:


IQueryable<Movie> FindByCategory(string category);

You have to go and edit all your implementations of your IMoviesQueryRepository (including all testing mocks) in order to be able to compile again your solution, then you add implementation of this new method in all of them.


So far so good, after a couple of days you realize that now you need a couple of filters more, let’s say that now you need to query by a list of categories and by the average cost of making each movie, so again you add the following methods to your interface:


IQueryable<Movie> FindByCategories(string[] categories);
IQueryable<Movie> FindByAverageCost(decimal averageCost);

And you repeat all the implementation process again.


This process will continue during the whole development lifecycle. And there are several filters that might be needed during this time for example, querying by rating, by amount of tickets sold, etc. etc.


So a design like this is not flexible, is not testable and is hard to maintain


Let’s see several problems with this design:


  • It violates the open-closed pricniple (open for extension/closed to modification).


    The problem when you don’t follow this principle is the problem described before, you will have to go and edit all your implementations in order to add new functionality. You could potentially introduce new bugs to the application that would affect more than just one query (perhaps all of them?)

  • A design like this can lead to violate the YAGNI principle (You Ain’t Gonna Need It)


    Why? Simply because in order to avoid the problem described before, you could try to think about all the possible filters that you might need in the application. This will lead you to write tons of query filters that you won’t ever need

  • This design is not test friendly I just wrote the result of these queries as IQueryable objects on purpose to ilustrate the point that this design is not test friendly, and actually you won’t be able to test in isolation your queries if you expose in your public API IQueryable objects. The reason is that your query logic will actually be spread out in several places in your application.


Let’s see some approaches that won’t work


Anti Unit Testing approach


The simplest approach is to forget about unit testing (oO) and consume the IQueryable object directly in all the places where you will need it (usually the user interface)


I don’t have to tell you that I would never recommend something like this. But surprisingly in Stackoverflow I've seen a lot of code like this, and a lot of examples over the internet about MVC using this approach.


Naive approach


Well let’s say that you realize that unit test is so important in any application that you decide to go back to the original design and try to figure out how to fix it.


So a really simplistic approach would be to call for a small meeting with all your developers and tell them that even when you expose an IQueryable object in your query repository they should never add new queries on it.... in order to “guarantee” that the query is encapsulated and therefore testable in isolation.....


….Good luck with that!


So now hopefully you will want to see the solution already... and here it is


This is my solution to the problem


Encapsulating the query


The first thing to do to refactor the original design is to encapsulate the query in order to guarantee that the query will actually be unit-tested in isolation. I’m going to follow the same approach I used in my last post, I will create a new DTO object to wrap my query results.


This object would look something like:


public sealed class QueryResults
{
    public static QueryResults<TTarget> Of<TTarget>(IList<TTarget> results, int virtualRowsCount)
    {
        Condition.Requires(results).IsNotNull();
        Condition.Requires(virtualRowsCount).IsGreaterOrEqual(0);

        return new QueryResults<TTarget>(results, virtualRowsCount);
    }
}

public class QueryResults<TTarget>
{
    public QueryResults(IList<TTarget> results, int virtualRowsCount)
    {
        Condition.Requires(results).IsNotNull();
        Condition.Requires(virtualRowsCount).IsGreaterOrEqual(0);

        this.VirtualRowsCount = virtualRowsCount;
        this.Results = results;
    }

    public int VirtualRowsCount { get; protected set; }

    public IList<TTarget> Results { get; protected set; }
}

Since the query will be encapsulated you also need an object to pass paging and sorting parameters:


public sealed class PagingAndSortingInfo
{
    public PagingAndSortingInfo(
        int page = 1,
        int pageSize = 10,
        string orderByField = "",
        OrderDirection orderDirection = OrderDirection.Ascending)
    {
        Condition.Requires(page).IsGreaterOrEqual(1);
        Condition.Requires(pageSize).IsGreaterOrEqual(1); ;

        this.Page = page;
        this.PageSize = pageSize;
        this.OrderByField = orderByField;
        this.OrderDirection = orderDirection;
    }

    public int Page { get; private set; }

    public int PageSize { get; private set; }

    public string OrderByField { get; private set; }

    public OrderDirection OrderDirection { get; private set; }
}

public enum OrderDirection
{
    Ascending,
    Descending
}

So your query repository would look like:


public interface IMoviesQueryRepository
{
    Movie FindByID(Guid movieID);
    QueryResults<Movie> FindAll(PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByTitle(string title, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByDirectors(string[] director, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByActors(string[] actors, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByReleaseDate(DateTime date, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByCategory(string category, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByCategories(string[] categories, PagingAndSortingInfo pagingAndSortingInfo = null);
    QueryResults<Movie> FindByAverageCost(decimal averageCost, PagingAndSortingInfo pagingAndSortingInfo = null);
}

Following this approach you will be able to unit test in isolation your queries, but you would still have the original problem.


Designing the Query Engine


OK, so now let’s design the query engine.


First of all I have a couple of interfaces that I’m using to mark my objects:


public interface IQuery
{
}

public interface IQueryID
{
}

  • The IQuery interface is the equivalent to the ICommand interface in the Commands branch in a CQRS architecture. This is used to mark value objects as query objects

  • The IQueryID interface is used to mark a value object as the query id. This will allows us to query by id using any type of id and even with composed id’s.


I created three interfaces in order to represent the queries in an application This is the core of the Query Engine:


public interface IQueryHandler<TQuery, TQueryResult> where TQuery : IQuery
{
    QueryResults<TQueryResult> HandleQuery(TQuery query, PagingAndSortingInfo pagingAndSortingInfo = null);
}

public interface IQueryHandler<TQueryResult>
{
    QueryResults<TQueryResult> HandleQuery(PagingAndSortingInfo pagingAndSortingInfo = null);
}

public interface IQueryByIDHandler<TQueryID, TQueryResult> where TQueryID : IQueryID
{
    TQueryResult HandleQuery(TQueryID queryID);
}

  • The IQueryHandler overload using the IQuery interface will be used to define explicitly the queries in your application

  • The IQueryHandler overload without the IQuery interface will be used to query all the records from the table

  • The IQueryByIDHandler will be used to query an entity/table/view by ID


This is a graphical representation:










Stay in touch, in the next post I will create base classes to encapsulate common behavior