Specification Generator
Generate DDD-compliant Specifications for encapsulating business rules and filtering logic.
Specification Characteristics
- •Single Responsibility: One business rule per specification
- •Composable: AND, OR, NOT combinations
- •Reusable: Same spec for validation and querying
- •Domain Language: Named using ubiquitous language
- •Testable: Easy to unit test in isolation
- •Immutable: No state changes after creation
When to Use Specification
| Scenario | Example |
|---|---|
| Business rule validation | IsActiveCustomer, CanPlaceOrder |
| Collection filtering | OverdueInvoice, PremiumProduct |
| Repository queries | OrdersByCustomer, ActiveUsers |
| Policy enforcement | EligibleForDiscount, CanBeShipped |
| Complex conditions | Composite AND/OR specifications |
Generation Process
Step 1: Generate Base Infrastructure
Path: src/Domain/Shared/Specification/
- •
SpecificationInterface.php— Generic interface withisSatisfiedBy() - •
AbstractSpecification.php— Base with AND/OR/NOT methods - •
AndSpecification.php— Composite AND - •
OrSpecification.php— Composite OR - •
NotSpecification.php— Negation wrapper
Step 2: Generate Concrete Specification
Path: src/Domain/{BoundedContext}/Specification/
- •
{Name}Specification.php— Implements business rule
Step 3: Generate Tests
Path: tests/Unit/Domain/{BoundedContext}/Specification/
File Placement
| Component | Path |
|---|---|
| Base Interface | src/Domain/Shared/Specification/ |
| Abstract Spec | src/Domain/Shared/Specification/ |
| Composites | src/Domain/Shared/Specification/ |
| Concrete Specs | src/Domain/{BoundedContext}/Specification/ |
| Unit Tests | tests/Unit/Domain/{BoundedContext}/Specification/ |
Naming Conventions
| Pattern | Example |
|---|---|
Is{Condition}Specification | IsActiveCustomerSpecification |
Has{Property}Specification | HasPurchaseHistorySpecification |
Can{Action}Specification | CanBeCancelledSpecification |
| Factory Method | IsOverdueInvoiceSpecification::now() |
Quick Template Reference
Specification Interface
php
/**
* @template T
*/
interface SpecificationInterface
{
public function isSatisfiedBy(mixed $candidate): bool;
public function and(self $other): self;
public function or(self $other): self;
public function not(): self;
}
Concrete Specification
php
/**
* @extends AbstractSpecification<{Entity}>
*/
final readonly class {Name}Specification extends AbstractSpecification
{
public function __construct({parameters}) {}
public function isSatisfiedBy(mixed $candidate): bool
{
if (!$candidate instanceof {Entity}) {
return false;
}
return {businessRule};
}
}
Composite Usage
php
$eligible = $isActive
->and($hasPurchases)
->and($isNotBlacklisted->not());
$customers = array_filter(
$all,
fn($c) => $eligible->isSatisfiedBy($c)
);
Anti-patterns to Avoid
| Anti-pattern | Problem | Solution |
|---|---|---|
| God Specification | Too many conditions | Split into composable specs |
| Side Effects | Modifies candidate | Keep pure, read-only |
| Infrastructure | DB calls in spec | Keep in domain, use for in-memory |
| Weak Typing | isSatisfiedBy(mixed) | Add type check first |
| No Composition | Copy-paste conditions | Use AND/OR composition |
References
For complete PHP templates and examples, see:
- •
references/templates.md— Interface, Abstract, Composite, Concrete templates - •
references/examples.md— Customer, Product, Invoice, Order specifications and tests