I\'m using a query object pattern (similar to this) to manage disparate queries
ID: 645890 • Letter: I
Question
I'm using a query object pattern (similar to this) to manage disparate queries while avoiding bloaded facades/repositories.
A query object takes a number of constructor parameters, representing query arguments. The query is then passed to an IQueryHandler by the caller, into which is injected the IDataContext. The IDataContext is then passed into an Execute method of the IQuery.
The thing I don't like is this:
public interface IQuery<TResult>
{
public TResult Execute(IDataContext context);
}
Because the IDataContext is passed into the method and thus explicitly declared in the interface, there is no option to have a query that fetches things via a different mechanism - IQuery is coupled to whatever defines the IDataContext interface. Suppose, for example, I want to switch from an SQL DB store to a document DB.
I've tried a couple of alternatives, but neither quite gets me where I want to be.
The first is to inject the data context into the query's constructor and use a factory to generate the query objects. This successfully decouples the interfaces, but now working with the queries is more cumbersome. Instead of using the constructor/object initializer to set up the query, callers have to do something like this:
var query = _queryFactory.Create<PersonQuery, Person>(); // Second type argument can't be inferred from first due to limitations in generic type inference.
query.Name = "Bob";
query.Age = 32;
var result = query.Execute();
The second is to abstract the execution of the query - and thus the dependency on IDataContext - to a handler class, then have another class that resolves a handler for each query, which again abstracts the dependency away from any interfaces:
public class ISomeQueryHandler : IQueryHandler<SomeQuery, SomeQueryResult>
{
private readonly IDataContext _context = ...;
public SomeQueryResult Execute(SomeQuery query) { ... }
}
I don't like this option because it means every new query involves writing two classes, which, again, is cumbersome and increases the potential for a query to exist with no way of handling it (it also involves an empty marker interface for queries since the Execute method is moved to the handler, which always feels off). It also involves some rather funky convention-based dependency resolution to get the right handler for each query.
Creating an abstraction over IDataContext is not really feasible due to its complexity.
Is there a happy medium I haven't seen?
Explanation / Answer
One solution I've used to this kind of problem is to add a layer of indirection to the context. In this case, I would rename IDataContext to ISQLDataContext, and create a new IDataContext with an "SQLContext" property to retrieve the existing context. Your query objects can then be trivially changed to use the new interface, and you can add new context types at the top level as you need them.
This solution isn't ideal - it doesn't conform to the open/closed principle with regards to adding new data stores, but (a) that is unlikely to be a common type of change and (b) as the implementation of the data context object will be very simple, most of the logic behind the OCP (i.e. that changing existing code is more likely to break something existing than adding entirely new code) simply doesn't apply.