{
    "slug": "specification_pattern",
    "term": "Specification Pattern",
    "category": "architecture",
    "difficulty": "intermediate",
    "short": "Encapsulating business rules as composable objects that evaluate whether a candidate satisfies them — separating rules from entity code.",
    "long": "The Specification pattern encapsulates a business rule as a class with an isSatisfiedBy($candidate): bool method. Specifications are composable: AndSpecification, OrSpecification, NotSpecification combine them with boolean logic. Example: ActiveCustomerSpecification->and(HasValidEmailSpecification) produces a compound rule. Benefits: business rules are named, reusable, and testable in isolation; they can be translated to query criteria (Doctrine Criteria or SQL WHERE clauses) for database-side filtering. PHP libraries: beberlei/specification, or implement the interface yourself (typically 10 lines). The pattern shines when the same rule must filter both in-memory collections and database queries — a Doctrine-aware specification generates DQL expressions while the core logic remains testable without a database.",
    "aliases": [
        "specification",
        "business rule encapsulation",
        "composable criteria"
    ],
    "tags": [
        "architecture",
        "ddd",
        "design-pattern",
        "oop"
    ],
    "misconception": "The specification pattern is just a fancy way to write WHERE clauses. Specifications encapsulate business rules as composable, reusable objects — they can be combined with and/or/not, used in both queries and in-memory filtering, and named to express domain concepts explicitly.",
    "why_it_matters": "The Specification pattern encapsulates business rules as composable objects — complex query conditions become named, testable, reusable specifications rather than SQL fragments scattered across repositories.",
    "common_mistakes": [
        "Specifications that leak SQL — they should express business rules, not WHERE clauses.",
        "Not making specifications composable with AND, OR, NOT — the pattern's core value is composition.",
        "Over-using specifications for simple, single-use queries — a repository method is simpler.",
        "Specifications that perform I/O — they should be pure predicates, not data fetchers."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "ddd_domain_services",
        "ddd_aggregates",
        "strategy_pattern",
        "query_optimisation"
    ],
    "prerequisites": [
        "domain_driven_design",
        "interfaces",
        "query_optimisation"
    ],
    "refs": [
        "https://en.wikipedia.org/wiki/Specification_pattern"
    ],
    "bad_code": "// Raw conditions scattered in repository:\npublic function findEligibleCustomers(): array {\n    return $this->db->query(\n        'SELECT * FROM customers WHERE active = 1 AND balance > 100 AND age >= 18'\n    )->fetchAll();\n}\n// 'Eligible' means different things in different contexts — use a specification:\n// $eligible = new ActiveSpec()->and(new MinBalanceSpec(100))->and(new AdultSpec());",
    "good_code": "interface Specification {\n    public function isSatisfiedBy(mixed $candidate): bool;\n}\n\nclass ActiveCustomer implements Specification {\n    public function isSatisfiedBy(mixed $customer): bool {\n        return $customer->isActive() && !$customer->isBanned();\n    }\n}",
    "quick_fix": "Encapsulate business rules as Specification objects with isSatisfiedBy($candidate) — combine with and()/or()/not() to build complex rules without giant if-statement blocks",
    "severity": "low",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-22",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/specification_pattern",
        "html_url": "https://codeclaritylab.com/glossary/specification_pattern",
        "json_url": "https://codeclaritylab.com/glossary/specification_pattern.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Specification Pattern](https://codeclaritylab.com/glossary/specification_pattern) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/specification_pattern"
            }
        }
    }
}