Using the Specification pattern to compose readable, extensible, maintainable and testable queries

Example scenario

I have data coming from a data source, e.g. a database or as the response from a third party HTTP API. I need to filter the data and pick parts that I'm after.

The naive way

Hard code the queries alongside logic that operates on the data.

Cons:

  • brittle queries. Every change has a likelihood of being hard to do and test

The specification pattern to the rescue

  1. The ISpecification interface
public interface ISpecification<T>
{
    bool IsSatisfied(T instanceOfT);
}
  1. The IFilter interface
public interface IFilter<T>
{
    IEnumerable<T> Filter(IEnumerable<T> sourceData, ISpecification<T> condition);
}
  1. Example:

public class ActivityTypeSpecification : ISpecification<Activity>
{
    public bool IsSatisfied(Activity activity)
    {
        if (activity.Type == "SERVICE")
        {
            return true;
        }

        return false;
    }
}

public class ActivityTypeFilter : IFilter<Activity>
{
    public IEnumerable<Activity> Filter(
        IEnumerable<Activity> activities, 
        ISpecification<Activity> activityTypeSpecification)
    {
        return activities.Where(act => activityTypeSpecification.IsSatisfied(act));
    }
}
  1. Combining Specifications
public interface AndSpecification<T> : ISpecification<T>
{
    private ISpecification<T> _first;
    private ISpecification<T> _second;

    public AndSpecification(
        ISpecification<T> first,
        ISpecification<T> second)
    {
        _first = first;
        _second = second;
    }

    public bool IsSatisfied(T instance)
    {
        return _first.IsSatisified(instance) && _second.IsSatisfied(instance);
    }
}