Specification
Specification captures a business rule as a reusable predicate object that reports whether a candidate satisfies it, and combines via and/or/not. It unifies validation, selection, and querying around domain rules expressed in one place.
Specification is a behavioral, domain-driven pattern that encapsulates a business rule in a dedicated object that can answer whether some candidate object satisfies the rule. By making rules first-class objects, you can name them, reuse them, test them in isolation, and combine them with boolean operators to build complex criteria from simple parts. It separates the statement of a rule from the code that applies it.
How It Works
A specification exposes a method such as isSatisfiedBy(candidate) returning a boolean. Composite specifications combine others through and, or, and not, each itself a specification, so rules form an expression tree. The same specification object can be used for three purposes: validating that an object meets a rule, selecting matching objects from a collection, and describing the criteria for objects to be created. Some implementations also translate a specification into a query, so the same rule that filters in memory can run in the database.
This dual use, in-memory evaluation and query generation, is what makes Specification especially valuable alongside repositories in domain-driven design.
When to Use It
Use Specification when business rules are reused across validation, selection, and construction, when rules must be combined dynamically, or when you want to keep rich domain logic out of services and queries and express it in the domain language. It fits eligibility checks, pricing and discount rules, search filters, and access policies.
Trade-offs
For a single, simple, one-off condition, a plain predicate or lambda is lighter than a full specification class. Combining many specifications can create deep object trees that are hard to read and may perform poorly if evaluated naively in memory. Translating specifications to efficient database queries is non-trivial and can leak persistence concerns into the domain. Overuse leads to a proliferation of tiny classes.
Related Patterns
Strategy also encapsulates logic in objects, but Specification specifically captures boolean criteria meant to be composed with and, or, and not. Repository commonly accepts specifications to fetch matching aggregates, often translating them into SQL or an ORM query so the same rule runs efficiently in the database. Composite provides the structure for combining specifications into larger rules, and Interpreter is closely related because a composite specification is effectively a small boolean expression tree. The pattern also pairs well with Value Object for the criteria parameters it carries.
Example
An e-commerce domain defines PremiumCustomer, OrderOverThreshold, and InStock as specifications, each a small class with isSatisfiedBy and unit tests of its own. A free-shipping rule becomes premiumCustomer.or(orderOverThreshold).and(inStock), assembled at runtime from configuration so marketing can adjust the promotion without a code change. The same composite checks eligibility at checkout, filters orders in a report, and validates incoming orders, and a repository can translate it into a query, keeping the rule defined in exactly one place. The pattern generalizes to insurance underwriting rules, content-moderation filters, fraud-scoring criteria, and access policies, anywhere business rules must be named, reused across in-memory and database contexts, and recombined as requirements evolve.