The Specification Pattern: compose business rules as reusable, testable predicates.
A Spec represents a single business rule that an object either satisfies or doesn't. Instead of embedding validation logic directly in methods, specifications are first-class objects that can be created, combined, tested, and reused independently.
This pattern is ideal for:
- Domain rules: Business rules, filtering, and search criteria
- Reusability: The same rule applies in multiple contexts without duplication
- Testability: Each rule is isolated and easy to unit test
- Composability: Combine simple rules into complex business logic
- Readability: Express business intent clearly through method names
Compose specs using:
&/ and — Both must be satisfied (AND logic)|/ or — At least one must be satisfied (OR logic)^/ xor — Exactly one must be satisfied, not both (XOR logic)- nand — At least one must not be satisfied (NAND logic)
- nor — Neither must be satisfied (NOR logic)
- xnor — Both must have the same satisfaction state (XNOR logic)
- negated or
~— Inverts the rule (NOT logic)
AND, OR, NAND, and NOR use left-to-right short-circuit evaluation. XOR and XNOR evaluate both operands because both results are needed.
Creating a Spec:
/// Business rule: person is old enough to vote
final class IsEligibleVoter extends Spec<Person> {
@override
bool isSatisfiedBy(Person person) => person.age >= 18;
}
/// Business rule: person has valid contact info
final class HasValidEmail extends Spec<Person> {
@override
bool isSatisfiedBy(Person person) => person.email.contains('@');
}
Using a Spec:
// Single rule
final isVoter = IsEligibleVoter();
if (isVoter.isSatisfiedBy(person)) {
registerToVote(person);
}
// Composed rules
final validVoter = IsEligibleVoter() & HasValidEmail();
final voters = people.where((p) => validVoter.isSatisfiedBy(p)).toList();
// Complex compositions
final canReceiveNewsletter = HasValidEmail() & (IsEligibleVoter() | HasOptedIn());
- Annotations
-
- @immutable
Constructors
- Spec()
-
Creates a specification.
const
-
Spec.allOf(Iterable<
Spec< specs)T> > -
Creates a spec satisfied when every spec in
specsis satisfied.factory - Spec.always()
-
Creates a spec that is satisfied by every candidate.
constfactory
-
Spec.anyOf(Iterable<
Spec< specs)T> > -
Creates a spec satisfied when at least one spec in
specsis satisfied.factory - Spec.never()
-
Creates a spec that is not satisfied by any candidate.
constfactory
-
Spec.noneOf(Iterable<
Spec< specs)T> > -
Creates a spec satisfied when none of the specs in
specsis satisfied.factory - Spec.predicate(bool predicate(T candidate))
-
Creates a spec from
predicate.constfactory
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
Methods
-
and(
Spec< T> other) → Spec<T> - Combines this spec with another using AND logic.
-
call(
T candidate) → bool -
Evaluates this spec for
candidate. -
contramap<
R> (T projection(R candidate)) → Spec< R> -
Adapts this spec to candidates of type
Rusingprojection. -
iff(
Spec< T> other) → Spec<T> -
Combines this spec with
otherusing logical equivalence. -
implies(
Spec< T> other) → Spec<T> -
Combines this spec with
otherusing implication logic. -
isSatisfiedBy(
T candidate) → bool -
Checks if
candidatesatisfies this specification. -
nand(
Spec< T> other) → Spec<T> - Combines this spec with another using NAND logic (NOT AND).
-
negated(
) → Spec< T> - Returns the logical negation of this spec.
-
nor(
Spec< T> other) → Spec<T> - Combines this spec with another using NOR logic (NOT OR).
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
or(
Spec< T> other) → Spec<T> - Combines this spec with another using OR logic.
-
toString(
) → String -
A string representation of this object.
inherited
-
xnor(
Spec< T> other) → Spec<T> - Combines this spec with another using XNOR logic (NOT XOR / equivalence).
-
xor(
Spec< T> other) → Spec<T> - Combines this spec with another using XOR logic (exclusive OR).
Operators
-
operator &(
Spec< T> other) → Spec<T> - Combines this spec with another using AND logic (&).
-
operator ==(
Object other) → bool -
The equality operator.
inherited
-
operator ^(
Spec< T> other) → Spec<T> - Combines this spec with another using XOR logic (^).
-
operator |(
Spec< T> other) → Spec<T> - Combines this spec with another using OR logic (|).
-
operator ~(
) → Spec< T> - Returns the logical negation of this spec.