Query Objects

Lets consider two situations mentioned below which define the problem.I guess lot of you would be familiar with the first one.

Problem

Situation 1 :

You start with a project which has a repository i.e. you have repository pattern implemented.Now initially situation would be something like shown below

public class ProductRepository : IProductRepository
    {
        public Product GetProduct(int Id)
        {
            //logic
        }
        public bool UpdateProduct(Product c)
        {
            //logic
        }

        public bool InsertProduct(Product c)
        {
            //logic
        }

        public bool DeleteProduct(Product c)
        {
            //logic
        }
    }

After 1 Year…


public class ProductRepository
 {
 public Product GetProduct(int Id)
 {
 //logic
 }
 public bool UpdateProduct(Product c)
 {
 //logic
 }

 public bool InsertProduct(Product c)
 {
 //logic
 }

 public bool DeleteProduct(Product c)
 {
 //logic
 }

 public List<Product> GetProductsByCategory()
 {
 //logic
 }

 public List<Product> GetProductsByDesignNo()
 {
 //logic
 }
 public List<Product> GetAllProductsWithComplaints()
 {
 //logic
 }
 public List<Product> GetProductsForCustomerWithComplaints(int customerId)
 {
 //logic
 }
 //////////////////////
 /// AND MANY MORE SUCH VARIATIONS OF GETTING PRODUCTS WITH DIFFERENT CRITERIA
 //////////////////////
 }

After 2 years …You can imagine.

Situation 2

You are developing a product with say advanced search functionality and you want to use along with your database a search server with its own query language.Only issue is that you want this search server functionality to be loosely coupled as your architects and management are not sure about the product and want to be able to replace it with a different search server in future.So the API to interact with this search functionality should provide complete abstraction of the specifics and should not leak any search product specific code in application.

Problem is that such products will have there specific query language and / or API.If I have to implement a SearchProducts method, how do I abstract the input query parameters / Objects so that I can present a uniform interface to code that uses this functionality. E.g. Elastic search provides you with a API which encapsulates search request in classes implementing ISearchRequest interface whereas Microsoft’s FAST provides a very elaborate REST API.

I have faced this situation in two of the projects i worked on and I am sure lot of people would have faced it. That’s what pointed me to Query objects ( or Query object Pattern).

Query Objects

This link has a brief explanation of query objects by Martin Fowler.

Query objects are basically named queries or queries represented by objects.It is equivalent to Command pattern for a query.

Lets see a simple implementation of query objects targeting Situation 2 above (same approach can be implemented for Situation 1).

First define a query interface which all our query classes will implement (below is an example of a query for search products by design no).

public interface ISearchQuery<T>
    {
        IEnumerable<T> Execute();
    }


public class FAST_ProductsByDesignNoQuery : ISearchQuery<IEnumerable<Product>>
    {

        private string _designNo;
        public FAST_ProductsByDesignNoQuery(string designNo)
        {
            _designNo = designNo;
        }

        public IEnumerable&lt;IEnumerable&lt;Product&gt;&gt; Execute()
        {
            //Specific query logic goes here
        }
    }

If we move to another search server than we will be defining a new query class for the same query implementing ISearchQuery interface.

Below is how our Search API  will look like.

public interface ISearch
 {
 IEnumerable<Product> SearchProducts(ISearchQuery<Product> query);
 IEnumerable<Customer> SearchCustomers(ISearchQuery<Customer> query);
 }
public class SearchAPI : ISearch
 {

 public IEnumerable<Product> SearchProducts(ISearchQuery<Product> query)
 {
 return query.Execute();
 }
 public IEnumerable<Product> SearchCustomers(ISearchQuery<Customer> query)
 {
 return query.Execute();
 }
 }

This makes my search API totally independent of search server which I am using.

The sample given above is the simplest example of Query objects but there is much more that can be done.

Going Further…

There are couple of things which we can do to make our query objects more sophisticated.We can have base class which adds paging support by adding properties related to page size,page number ,sorting etc.

Taking it to extreme we can define generic query objects where queries can be defined using project specific language rather using separate classes for each query and use interpreter for translating them to data source specific queries.And Yes, you are right,Expression trees in .NET is a good example query objects (provided you have an interpreter to translate expression tree to your data source specific query ).Another example is Hibernate’s Criteria API.

Only thing to be careful about above approach  is how complex you want this to become.For example having set of classes which define your project specific query language or writing custom interpreter for expression trees is quite complex and  does not make sense unless you are working on a very big project being implemented by multiple team.

Additonal Resources

You wont find many books or articles on this topic but here is a link to book “Professional ASP.NET Design Patterns” which in general talks about architecture / design patterns used in ASP.NET (also talks about query objects).Apart from that here is another good article on this topic.

Tagged on: ,

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>