The specification pattern is a simple design pattern that basically says:
Specification of which objects satisfy certain business rules should be reusable (DRY). In order to do that we will create a class with sole responsibility of determining, whether object satisfies the rules or not.
If you are interested in how and why you should use it, there is an excellent article on these details specification pattern at the Enterprise Craftsmanship.
While great article, there are some points about the pattern that are worth emphasizing/mentioning.
Specifications have a name
Mundane, right. But very, very important aspect. Specifications have a name in source code and that makes your code readable. Compare
var payments = repository.Query(new OverduePayementsSpecification(TimeSpan.FromDays(10));
with
var now = Clock.UtcNow; var payments = repository.Query(p => p.PaymentDate.Subtract(now) >= TimeSpan.FromDays(10))
In one case the purpose of the code is obvious, in other, it is not.
The business rule should be important enough to have its own name and type.
Specification pattern keeps business rules in business layer
If you are using onion mode, hexagonal architecture or basically any kind of architecture, where business is in the center of your architecture, specification pattern is in your business project.
The business layer contains interfaces and the implementation of the interfaces is outside, probably in some kind of infrastructure layer.
Thus comparing use of a method at your data access object
var payements = repository.GetOverduePayements(TimeSpan.FromDays(10));
with the specification pattern
var payments = repository.Query(new OverduePayementsSpecification(TimeSpan.FromDays(10));
will result into having your business rules in your infrastructure layer.
That is just wrong, what if you need to run your software once on your on-premise PostgreSql (or some kind of other Db) and other instance for cloud customers on Azure SQL (very common licencing model). Do you duplicate all your business rules? If you do, I predict failure and plethora of bugs and maintenance nightmare.
Specification Transformations
ORM mappers have advanced quite a bit, but even EF Core still can’t query a NodaTime propery, (or any kind of custom property, e.g. CustomerId instead of int/guid) which means that object-relational impedance is still alive and kicking strongly.
In my projects, I don’t use EF entities (or NHbernate or any ORM mappter) directly, but I use T4 template to translate business classes into partial classes with sensible properties and types.
This has some advantages
- ORM entities can have more properties than business objects, e.g. transaction entity (code-first) has all properties received from the import file, but business only ~10 that are useful.
- Eliminates impedance
- Keeps infrastructure in infrastructure
and disadvantages:
- mapping logic is necessary
The specification should be written using business classes, because it is a business rule. However, in order to query your data store, you need to translate the specification expression into an expression on ORM entities.
I recommend AutoMapper, specifically its queryable extensions and expression translation. It helps to keep mapping logic to minimum.
Specification projections
Most examples of the pattern end up with a specification of an object, but don’t show that you how to get a related object. That is especially troubling in case of DDD aggreates, where there are no navigation properties to the outside of aggregate, only ids of other aggregates.
I have a specification of overdue payement… how do I get clients with an overdue payments?
Fear not, it is solvable. Most examples show examples of AndSpecification (or some other kind of logic operation on two specifications) to demonstrate versatility of the pattern.
But there is more! Although in this case, you have to add a method to repository and voila> thanks to magic of projection, you can use specification of aggregate to select completely different aggregate.
public class Entity {
public int Id { get; set;}
}
public class Client : Entity{
public string Name {get;set;}
}
// Paymeent has no navigation property to Client, only Id of payer.
// Without navigation property, you can't just write an expresion to check.
public class Payment : Entity {
public LocalDate PayementDate { get; set; }
public int PayerId { get; set; }
}
class OverduePaymentSpecification : Specification<Payment> {
Expression<Lambda<Payment, bool>> AsExpression() {/* implementation*/ }
}
public class Repository<TEntity> where TEntity : Entity
{
public IList<TEntity> ProjectQuery<TSpecifiedEntity>(
Specification<TSpecifiedEntity> specification,
Expression<Func<TSpecifiedEntity, int>> idSelection)
{
var projectedEntities = GetDbSet<TEntity>();
var specifiedEntities = GetDbSet<TSpecifiedEntity>();
return specifiedEntities
.Where(specification.AsExpression())
.Join(projectedEntities, idSelection,
projectedEntity => projectedEntity.Id,
(specifiedEntity, projectedEntity) => projectedEntity)
.ToList();
}
}
// Example of usage
var clientRepository = new Repository<Client>(context);
var clientsWithOverduePayments = clientRepository
.ProjectQuery(new OverduePaymentSpecification(), payment => payment.PayerId);
That’s all. I like specification pattern, because thanks to magic of EF and Automapper, I am mostly left with a standard AutoMapperRepository and I don’t have to program business logic into infrastructure.
Sidenote: Personally, I am not too fond of using “chains” of specifications, but is quite useful pattern. I am working with systems that have a lot of weird business rules and having an separate class