Stackbook Logo
decision-makingestablished · low operational burden

Specification Pattern

Also known as: specification, business-rule-specification

Intent

Encapsulate a business rule as a composable object that can test, select, or construct domain objects.

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.

Forces

  • •The same rule is needed for validation, filtering, and creation
  • •Rules should be named in business language
  • •Rules must combine without rewriting
  • •In-memory checks and database queries must agree

Solution

Overview

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.

How it works

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.

Pitfalls

  • Fetching everything and filtering in memory, which collapses at scale.
  • Maintaining separate in-memory and SQL versions that drift apart.
  • Putting data access inside isSatisfiedBy, which makes rules slow and hard to test.
  • Creating specifications for trivial checks.
  • Naming by technical shape rather than business meaning.

Key takeaways

  • Name the rule in business language.
  • Compose small specifications instead of nesting conditionals.
  • Keep one definition for both checking and querying.
  • Use it where rules repeat, not everywhere.

✓ When to Use

  • •Rules reused across validation and selection
  • •Business rules that change often or vary by customer
  • •Complex filter combinations needing readable composition

✗ When Not to Use

  • •One-off simple conditions
  • •Performance-critical queries needing hand-tuned SQL
  • •Small codebases where plain methods are clearer

Pros

  • +Rules named, reusable, and testable
  • +Composition replaces conditional sprawl
  • +Single source of truth for criteria

Cons

  • −More classes for simple rules
  • −Translating to queries can be limiting or inefficient
  • −Over-abstracting small conditions

Cost Profile

Infrastructure

None — code pattern

Operational

Low

Cognitive

Medium — functional composition style

Failure Modes

  • 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

Real-World Examples

Alternatives

    Related Patterns

      Competency Domains

      requirements constraintsdata state