Problem
Business criteria such as eligible customer or overdue invoice get duplicated in validation, queries, and UI code, and drift apart. Complex conditions become unreadable boolean expressions.
Also known as: specification, business-rule-specification
Encapsulate a business rule as a composable object that can test, select, or construct domain objects.
Business criteria such as eligible customer or overdue invoice get duplicated in validation, queries, and UI code, and drift apart. Complex conditions become unreadable boolean expressions.
The specification pattern comes from Eric Evans and Martin Fowler. A specification answers one question: does this object satisfy a criterion? Because specifications are objects, they can be named, passed around, stored, and combined.
Define small specifications and compose them to express larger rules.
interface Spec<T> { isSatisfiedBy(t: T): boolean }
class OverdueInvoice implements Spec<Invoice> {
isSatisfiedBy(i: Invoice) { return i.dueDate < today() && !i.paid }
}
const reminderCandidates = and(new OverdueInvoice(), new ActiveCustomer())
Spec A --and--> Spec B --not--> Spec C
used by: validation | repository filter | object creation
For repositories, a specification can expose a predicate or query fragment, so the call becomes repo.find(reminderCandidates). The key discipline is generating the query from the same definition rather than rewriting the rule in SQL by hand.
None — code pattern
Low
Medium — functional composition style
In-memory and SQL versions of a rule diverging
Loading all rows to evaluate a specification in memory
Specifications with hidden side effects or I/O