Skip to content

SpecificationAdvice

Craig Fowler edited this page Mar 5, 2020 · 3 revisions

Here are some snippets of advice relating to the use of the specification pattern.

Prefer expressions over functions

Specification expressions are more reusable than functions; so they are almost always the superior choice. Any time you find yourself writing a specification function, consider "Could this be refactored to become an expression?".

Avoid specifications for single-property tests

Specifications which only test a single property against a constant value are rarely worthwhile coding. For example in an application which operates solely in the UK, a specification CanOrderBeer which performs the test x => x.Age >= 18 would be of little value.

On the other hand, imagine if this application operated internationally, where the legal age to buy beer differs per jurisdiction. In that case a specification like the following example would be worthwhile.

using CSF.Specifications;
using System.Linq.Expressions;

public class CanOrderBeer : ISpecificationExpression<Person>
{
    readonly IGetsMinimumAgeToPurchaseAlcoholForJurisdiction legalAgeProvider;
    readonly string jurisdictionCode;

    public Expression<Func<Person,bool>> GetExpression()
    {
        var minAge = legalAgeProvider.GetMinimumAgeToPurchaseAlcohol(jurisdictionCode);
        return person => person.Age >= minAge;
    }

    public CanOrderBeer(IGetsMinimumAgeToPurchaseAlcoholForCulture legalAgeProvider, string jurisdictionCode)
    {
        this.legalAgeProvider = legalAgeProvider ?? throw new ArgumentNullException(nameof(legalAgeProvider));
        this.jurisdictionCode = jurisdictionCode ?? throw new ArgumentNullException(nameof(jurisdictionCode));
    }
}

Useful specifications encapsulate some business/domain logic, they should not just test values as if they were flags.

Prefer specification classes over dynamic specifications

It is possible to use this library to create dyanmic/ad-hoc specification objects. Usually, though, it is best to use specification classes. This way the logic is encapsulated and reusable as a single self-contained concept.

If specifications are regularly composed together, do so in a class

It is perfectly reasonable to create a specification class which does no more than compose other specifications. This is particularly useful where the composition of specifications is in fact a reusable piece of logic in its own right. This is also a case where a dynamic specification might be useful; just for the purpose of composition. Take a look at the example below.

using CSF.Specifications;
using System.Linq.Expressions;

public class InvoicesToSendForDebtCollection : ISpecificationExpression<Invoice>
{
    public Expression<Func<Invoice,bool>> GetExpression()
    {
        var invoicesThatArentWrittenOff = Spec.Expr<Invoice>(x => !x.IsWrittenOff);
        var overdueInvoices = new InvoicesThatAreOverdue();
        var alreadyInDebtCollection = new InvoicesThatAreWithDebtCollection();

        return overdueInvoices
            .And(invoicesThatArentWrittenOff)
            .And(alreadyInDebtCollection.Not())
            .GetExpression();
    }
}