Using the Specification Pattern for Querying


The specification pattern is great for adhering to the Single Responsibility Principle (SRP). The reason it can be so powerful is that it encapsulates one piece of logic, and nothing more. I’ve decided to come up with some code that takes advantage of this very easily readable and maintainable code structure.

What Is the Specification Pattern

At the time of writing this, Wikipedia has a pretty good code example for the Specification Pattern in C#. I modified this a bit to use generics so that I can use the pattern with any Entity. Here’s my take:

All of my custom Specifications will derive from the CompositeSpecification. In doing so, whenever I call .And(), .Or(), or .Not() on my object, I’m returning another specification. This allows me to chain them together to create complex business rules that involve several rules that could be nested toegether.

My examples will only use Specifications along with my Customer class, but I could use this same pattern with Orders or LineItems if the need arose.

public interface ISpecification<T>
    {
        bool IsSatisfiedBy(T candidate);
        ISpecification<T> And(ISpecification<T> other);
        ISpecification<T> Or(ISpecification<T> other);
        ISpecification<T> Not();
    }
    public abstract class CompositeSpecification<T> : ISpecification<T>
    {
        public abstract bool IsSatisfiedBy(T candidate);

        public ISpecification<T> And(ISpecification<T> other)
{
            return new AndSpecification<T>(this, other);
        }

        public ISpecification<T> Or(ISpecification<T> other)
{
            return new OrSpecification<T>(this, other);
        }

        public ISpecification<T> Not()
{
            return new NotSpecification<T>(this);
        }
    }
    public class AndSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> left;
        private readonly ISpecification<T> right;

        public AndSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
        }
    }
    public class OrSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> left;
        private readonly ISpecification<T> right;

        public OrSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
        }
    }
    public class NotSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> other;

        public NotSpecification(ISpecification<T> other)
        {
            this.other = other;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return !other.IsSatisfiedBy(candidate);
        }
    }

All of my custom Specifications will derive from the CompositeSpecification. In doing so, whenever I call .And(), .Or(), or .Not() on my object, I’m returning another specification. This allows me to chain them together to create complex business rules that involve several rules that could be nested toegether.

My examples will only use Specifications along with my Customer class, but I could use this same pattern with Orders or LineItems if the need arose.

Types of Specifications

In these examples, I’ve created some customer specifications so that I can query my Customers based on the criteria that the business has set aside as the rules for defining Customer status. I have a Preferred Customer, Gold Customer and Platinum Customer. With these specifications in place, we can even extend our Customer class (with or without extension methods) to determine if they fit the criteria. Again, this keeps our code in one place, so any changes won’t need to be duplicated across the project.

public Customer
    {
        public bool IsPreferredCustomer()
        {
            return new PreferredCustomerSpecification().IsSatisfiedBy(this);
        }
    }

I probably wouldn’t add this property unless it was needed, but it shows how to keep your business logic for determining a preferred customer in one place.

The customer status specifications are based on inheritance. You have to meet the previous criteria AND meet some new criteria to be satisfied by the next “tier”. These are fairly standard and have the benefit of being easy to write and be very expressive, but have the drawback of being quite inflexible. The other specification in my examples that is a Hard Coded Specification (as seen in Martin Fowler’s Specifications paper) is the BadCustomerSpecification.

public class BadCustomerSpecification : CompositeSpecification<Customer>
    {
        public override bool IsSatisfiedBy(Customer entity)
        {
            return (entity.AccountCredit < 0m);
        }
    }

The SuspiciousCustomerSpecification is a parameterized specification. This can allow for a bit more flexibility than a hard coded specification since you’re able to pass in some data or objects. While this is a bit more flexible, it still requires special purpose classes.

public class SuspiciousCustomerSpecification : CompositeSpecification<Customer>
    {
        private readonly IEnumerable<long> suspiciousCustomerIDs;

        public SuspiciousCustomerSpecification(IEnumerable<long> suspiciousCustomerIDs)
        {
            this.suspiciousCustomerIDs = suspiciousCustomerIDs;
        }

        public override bool IsSatisfiedBy(Customer entity)
        {
            return suspiciousCustomerIDs.Contains(entity.ID);
        }
    }

In the SuspiciousCustomerSpecification we can pass in a list of customers that have been flagged due to activity, or other reasons.

More Thoughts on Specification Querying

While these examples are a bit more advanced that the typical specification which with I’ve dealt. There is much, much more that you can do with this design pattern. It’s important to understand that you shouldn’t implement a pattern just to do so, use them when the situation fits the pattern that is evolving from the problems you’re facing. Specifications should adhere to a single rule that matches user voice.

If you can’t describe it in the user’s language, you should stop and think about whether or not you’re over engineering.

and…

Staying in user voice makes it easier to ask a user how something in the system SHOULD behave, when you are unsure.

These two quotes are from a blog post by Lee Brandt at Codebucket.com and I they are imperative to creating specifications.

I’m A Lucky Programmer Too!