Specification pattern
Encapsulate business rules as composable, reusable objects that can be combined with AND, OR, and NOT operators for flexible filtering and validation.
Introduction
The Specification pattern represents a business rule as an object with a boolean contract. Leaf specifications check one rule; composite specifications combine them with AND, OR, and NOT. A caller can reuse the resulting rule tree in search, validation, authorization, or another domain operation.
TL;DR / mental model
Think of a specification as a reusable sentence fragment: cheap.and(electronics).and(inStock). The consumer evaluates the composed rule without knowing how it was built, while each leaf remains focused on one business condition.
Problem and Context
You're building a product catalog. The search page needs filters: price range, category, in-stock only, minimum rating, brand. Without a pattern, every new filter means reopening the same method and adding another condition.
Four filters today, twelve next quarter. Every new filter forces a parameter change on every caller. Testing one rule means constructing the entire parameter list with nulls for the ones you don't care about. The method signature grows until nobody can read it.
The deeper problem: the business rules are scattered across a single method. You can't reuse "in stock" logic in the order validator, the recommendation engine, or the admin dashboard without copy-pasting.
Here is what changes when you apply the Specification pattern.
Participants and Structure
Specification<T> is the core interface with a single boolean method: isSatisfiedBy(T). Each business rule (price cap, category match, stock check) is its own class. The composite specifications (AndSpecification, OrSpecification, NotSpecification) combine rules using boolean algebra. Default methods on the interface let you chain them fluently: priceSpec.and(categorySpec).or(inStockSpec).
This is the Composite pattern applied to boolean predicates. Each leaf spec checks one rule, and the composite specs compose them into arbitrarily complex conditions.
Idiomatic Example and Implementation Notes
A common fit is filtering or validation logic reused across multiple contexts. The key insight is that each rule becomes an object you can combine like a building block.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.
Related Articles
The strategy pattern extracts a family of algorithms behind an interface so the client can swap behaviors at runtime without touching the context class.
Learn how the composite pattern lets you treat individual objects and groups uniformly, building tree structures where clients never distinguish leaves from branches.
Chain of responsibility passes a request along a handler pipeline until one processes it. Decouple senders from receivers with composable, reorderable handler chains.