Implementing the Specification Pattern via Linq
_Originally published as http://ubik.com.au/article/named/implementing_the_specification_pattern_with_linq | July 06, 2008 10:23._ |
Linq came with grand promises of bridging the object and relational worlds. Here’s one place that it succeeds very well.
Queries and Predicates
Queries have historically sat uneasily alongside predicates in domain-driven designs.
The point of discomfort is that logic needs to be duplicated in different representations for the dual purposes of evaluation an data access.
This is best illustrated by an example - in this case from a library system:
public class Loan
{
// Some details omitted
public DateTime DateBorrowed { get; }
public DateTime? DateReturned { get; set; }
public bool IsOverdue
{
get
{
return DateReturned == null &&
DateTime.Now - DateBorrowed > LoanPeriod;
}
}
}
The IsOverdue
property encapsulates some of the logic from our domain so that it can be utilised within the domain model. Whether or not a loan is overdue is probably going to be significant in places dealing with the individual loan - when it is displayed, or when the borrower seeks to renew their library membership.
Another typical use case for such a system might be an ability to bring up a list of overdue books. To implement this efficiently, the IsOverdue
predicate needs to be expressed in a form that can select records from the database (here using NHibernate.Linq):
public class LoanRepository
{
public IEnumerable<Loan> FindOverdueBooks()
{
return from loan in _session.Linq<Loan>()
where loan.DateReturned == null
where DateTime.Now - DateBorrowed > Loan.LoanPeriod
select loan;
}
}
This is obviously simplified, but you can see that even when using Linq it is still necessary to duplicate theIsOverdue
predicate in a form that can be used to query the database.
In his book Domain Driven Design, Eric Evans discusses a pattern that can come to the rescue, and when implemented using Linq, is better than ever.
Specification
The essence of Specification is that the logic is extracted from the entity into a specification object:
The specification is part of the domain model, but gets used by the data access layer to find matching items.
In this example, the specification is for overdue loans, so it might be represented by anOverdueLoanSpecification
.
Within the domain model, the specification is used as a predicate:
public class Loan
{
public bool IsOverdue
{
get
{
return new OverdueLoanSpecification().IsSatisfiedBy(this);
}
}
}
Within the data access layer, the specification is used as a criterion:
var loanRepository = new LoanRepository();
var specification = new OverdueLoanSpecification();
var overdueLoans = loanRepository.FindBySpecification(specification);
Implementing with Linq
So, how can OverdueLoanSpecification
be implemented in a DRY and optimal manner for both purposes?
The answer turns out to be simple, and is a testament to the incredible elegance of the design of Linq.
First an abstract specification class:
public abstract class Specification<T>
{
public bool IsSatisfiedBy(T item)
{
return SatisfyingElementsFrom(new[] { item }.AsQueryable()).Any();
}
public abstract IQueryable<T> SatisfyingElementsFrom(IQueryable<T> candidates);
}
Implementers only need to define the query-based version - the predicate version is implemented on top of it by the base class.
The OverdueLoanSpecification
is part of the domain model:
public class OverdueLoanSpecification : Specification<Loan>
{
public override IQueryable<T> SatisfyingElementsFrom(IQueryable<T> candidates)
{
return from loan in candidates
where loan.DateReturned == null
where DateTime.Now - DateBorrowed > Loan.LoanPeriod
select loan;
}
}
Using a specification within a repository is straightforward, efficient, and preserves all of the lazy-evaluation goodness that Linq brings:
public class Repository<T>
{
public IQueryable<T> FindBySpecification(Specification<T> specification)
{
return specification.SatisfyingElementsFrom(_session.Linq<T>());
}
}
This example captures the essence of the solution, but there are obviously plenty of refinements that should be made in practice. I’ve started building a more complete an example as part of the Autofac example application - the curious can tinker with a very early version from the Autofac Subversion repository.
(The Autofac example is barely even a skeleton and probably shouldn’t be construed as ‘best practice’ at this stage.)
Advantages
I can see the following advantages of this implementation:
-
Logic stays in one place, in the domain model, where it belongs
-
Specifications are readily testable even without a database present
-
Specifications are composable: chaining two calls to
SatisfyingElementsFrom()
will result in only a single database query -
Interface bloat can be avoided in both the entity and the repository
Downsides?
-
Potential circularity in the entity/specification relationship
-
Unknown performance ramifications of
AsQueryable()
under heavy usage -
A little bit less encapsulation in the entity class
I’ll definitely be using this pattern in the future - I’d love to hear of anyone’s experiences in using it.
Links from Original Comments