skip to content

Specification Pattern

Wrap a selection rule in an object so it can be named, tested, and combined with AND, OR and NOT. You will learn its three uses — validation, selection and construction-to-order — and how specifications get translated into repository queries.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

6

In Domain-Driven Design, what is the Specification pattern, and what problem does it solve compared to scattering `if` conditions across your codebase?

level: juniorimportance: must knowfreq 55%

answer

  1. isSatisfiedBy(candidate)
  2. one rule, one home
  3. predicate as domain object
  4. vs inline if scattering

basics

~20 s

A Specification is an object that holds a business rule ("is this order overdue?") and can answer yes/no for any candidate. Instead of copy-pasting the same if-check in five places, you write it once as an object and reuse it everywhere.

solid answer

~30 s

A Specification wraps a predicate - "does this candidate satisfy this business rule?" - as a first-class domain object with a method like `isSatisfiedBy(candidate)`. It solves the problem of business rules being duplicated and scattered as inline conditionals across services, controllers, and queries. By naming the rule (e.g., `OverdueInvoiceSpecification`) and giving it a single home, you get a reusable, testable, composable unit that speaks the ubiquitous language, instead of the same boolean logic drifting out of sync in multiple places.

go deeper

for a junior

Should be able to state that a Specification names and encapsulates a business rule as a reusable object, and give a plausible reason (avoiding duplicated ifs) without needing to discuss composition or query translation yet.

for a middle

Should articulate the isSatisfiedBy contract, give a concrete named example, and recognize the reuse/testability motivation as the core driver, not just 'it's a pattern.'

for a senior

Should weigh when NOT to use it (one-off checks), distinguish it from a private helper method, and connect it to composability and to repository query translation as the pattern's real payoff.

for a principal

Should discuss it as a modeling decision that trades indirection for a single source of truth on a business rule, and know the over-application failure mode (Specification-itis) as a real design smell to push back on in review.

## What a Specification is The Specification pattern, as popularized by **Eric Evans** and **Martin Fowler**, takes a boolean business rule — "is this candidate valid/selected/eligible?" — and turns it into an object rather than leaving it as inline code. Mechanically, a Specification exposes one core operation, commonly named `isSatisfiedBy(candidate): boolean`, which takes a domain object and returns whether it matches the encapsulated rule. For example, instead of writing `if (order.status == PAID && order.dueDate.isBefore(today))` inline in three different services, you create an `OverdueInvoiceSpecification` class whose `isSatisfiedBy(order)` method contains that exact expression once, given a name that reads as domain language. ## The problem it solves Why does this exist? Business rules that determine "is this a good candidate" tend to multiply and drift. A junior team member adds an order-eligibility check in the checkout flow; six months later, someone else needs the same rule for a nightly batch report and reimplements it slightly differently (maybe forgetting a currency check, or using `<=` instead of `<`). Now there are two divergent definitions of "eligible order" living in the codebase, and nobody notices until a bug report surfaces the discrepancy. The Specification pattern fixes this by giving the rule exactly **one home**: - one class - one name - one test suite Every consumer — validation code, filtering code, UI enablement logic — depends on that one object instead of re-deriving the logic. ## The trade-off The trade-off is **indirection**. A single `if` statement is trivial to read in place; a Specification adds a class, a constructor, and a method call, which is overkill for a truly one-off check used in exactly one place. The pattern earns its keep specifically when a rule: - **(a)** is reused in more than one place; - **(b)** needs to be composed with other rules; - **(c)** needs to be tested in isolation from the surrounding workflow; - **(d)** needs to be passed around as data (e.g., handed to a repository to select matching records). If none of those apply, wrapping a boolean expression in a class is needless ceremony and adds a layer of indirection a reader has to jump through to understand what's actually being checked. ## Failure modes Failure modes show up in two directions. - **First, under-application:** teams write ad hoc booleans everywhere and the rule genuinely does drift, producing subtle correctness bugs where "overdue" means something different in the billing job than in the customer-facing dashboard — exactly the problem the pattern targets. - **Second, over-application:** teams wrap every trivial boolean check into its own Specification class "because DDD," producing an explosion of tiny classes (`IsPositiveAmountSpecification`, `HasNonNullEmailSpecification`) that add navigation overhead without adding reuse or composability — readers now have to open three files to understand one condition that used to be a single readable line. The sweet spot is rules that are genuinely part of the domain's vocabulary (an underwriter's "is this applicant insurable" rule, a marketplace's "is this listing eligible for boost") and that get combined or reused, not utility-level null checks. ## A worked example A concrete, well-known scenario: an e-commerce platform's promotions engine needs to know whether a cart qualifies for free shipping. The rule — "cart total exceeds $50 AND the customer is in a supported region AND no item in the cart is oversized" — is genuinely business logic, changes over time as marketing adjusts the threshold, and is consulted from multiple places: - the cart page (to show a progress bar) - the checkout flow (to actually apply free shipping) - a nightly analytics job (to measure how many carts nearly qualified) Wrapping this as a `FreeShippingEligibilitySpecification` gives the business rule one authoritative implementation, one place to update when the threshold changes from $50 to $75, and one thing product/QA can point to when asking "why didn't this order get free shipping?" Contrast this with a specification wrapping `amount > 0` used exactly once in a form validator — here a plain `if` is clearer and the class only adds friction. ## What it is not It's worth being precise about what the Specification pattern is not: it is not the same as the generic Gang-of-Four "Strategy" or "Composite" pattern, even though it's frequently implemented using both (composability via Composite, pluggable rule objects via Strategy). It is a **domain-modeling idiom** specifically about naming and encapsulating a business predicate so it can be validated against, selected by, and eventually translated into a query, which is what distinguishes it from a bare predicate function or lambda: the Specification is a first-class, named, potentially serializable domain concept, not just a closure.

  • Would you write a Specification for a check used in exactly one place and never reused?
    Generally no - the value of a Specification comes from reuse, composability, or the need to test/pass the rule independently of its call site. A one-off boolean is clearer as an inline `if`; wrapping it in a class adds indirection without buying anything, and a reviewer now has to jump to another file to see a single comparison.
  • How is a Specification different from just extracting the condition into a private method?
    A private method is scoped to one class and can't be composed, passed around, or reused elsewhere without copy-pasting. A Specification is a standalone object, so it can be combined with AND/OR/NOT, stored, handed to a repository, or unit-tested in isolation - none of which a private helper method supports.
  • Does the rule inside a Specification have to be pure/side-effect-free?
    Yes, in practice `isSatisfiedBy` should be a pure predicate - it evaluates the candidate and returns a boolean without mutating state or performing I/O. Side effects would make the Specification unsafe to reuse across validation, selection, and query-translation contexts, since those contexts may call it differently (e.g., translate it to SQL rather than execute it).

A Specification is like a bouncer's guest list rule written on an index card ("must be 21+ and on the list") - instead of every doorman at every entrance memorizing and possibly misremembering the rule, they all check the same card.

saying these in an interview costs you the question

  • Wraps every trivial null-check or `> 0` comparison in its own Specification class
  • Can't explain why the rule needs to be an object instead of a boolean method
  • Confuses Specification with the generic GoF Strategy pattern with no domain framing
  • Puts I/O or persistence calls inside isSatisfiedBy
  • Treats it as just a fancy validator with no composability need

context

open as a page

How do composable specifications work - what does it mean to combine two Specification objects with AND, OR, and NOT, and why is that composability valuable?

level: middleimportance: must knowfreq 60%

basics

~20 s

You can glue simple rules together like Lego: "expensive" AND "overdue" makes a new rule that's true only when both are true. Each combined rule is itself a Specification, so you can keep combining without ever rewriting the original checks.

open as a page

When a Specification's isSatisfiedBy logic needs to run as a database query instead of filtering an in-memory list, what's the core translation challenge, and how do teams typically solve it without duplicating the rule?

level: seniorimportance: must knowfreq 65%

basics

~20 s

Checking one object in code and asking a database "give me all matching rows" are different jobs. If you write the rule twice - once as code, once as SQL - they can drift apart. Teams solve this by having the specification generate the query itself instead of writing it separately.

open as a page

Specifications are traditionally described as having three uses: validation, selection, and construction-to-order. What does 'construction-to-order' mean, and how does it differ from just validating an object after it's built?

level: middleimportance: should knowfreq 45%

basics

~20 s

Validation checks an object after it exists. Construction-to-order uses the same rule before building, to describe exactly what a new object should look like so a factory can build one that already satisfies the rule, instead of building something and then rejecting it.

open as a page

When you use a composite Specification purely for validation (e.g., rejecting an invalid domain object before it's saved), how do you get a useful, itemized error message instead of just a single true/false result?

level: seniorimportance: should knowfreq 40%

basics

~20 s

A single yes/no doesn't tell the user WHY something failed. So instead of just isSatisfiedBy returning false, you add a way for each sub-rule to report its own failure reason, and collect all of them into a list the user can actually read.

open as a page

As a tech lead reviewing a design that proposes composable Specification objects for every filtering and validation need across a large codebase, what trade-offs and failure modes would make you push back, and when is a simpler predicate/query-object approach the better call?

level: principalimportance: should knowfreq 35%

basics

~20 s

Specifications add extra classes and indirection. If a team uses them for every tiny check, the codebase gets harder to read and navigate for little benefit. Use them where rules are genuinely reused, combined, or need to become queries - not everywhere.

open as a page