Search by

antevemus / aspecification

helitonjr

Enterprise Specification Pattern Framework for PHP 8.2+ (DDD, Notification Pattern, SQL Query Visitor, Adianti TCriteria, DAG Repositories)

v2.12.0 2026-11-01 21:56 UTC

README

🇺🇸 English  |  🇧🇷 Português (Brasil)

CI PHP Version Latest Version License: MIT Tests Architecture Upstream: Domian Synergy: ALinq

Enterprise Specification Pattern Framework for PHP 8.2+ (PHP 8.4 only for the optional ALinq integration)
Independent, unofficial PHP reimplementation and evolution of the acclaimed Java Domian framework, grounded in the seminal paper Specifications by Eric Evans and Martin Fowler. Enhanced with natural-language fluent chaining, zero-exception Notification Pattern with rich diagnostic telemetry, relational database Dynamic Rule Engine, multi-DBMS SQL Query Visitor (15 drivers, 10 SQL dialects), Adianti Framework TCriteria Builder, Directed Acyclic Graph (DAG) partitioned repositories, hybrid persistence, high-concurrency IPC primitives, a specification-driven state machine, use cases with a result shared between both, concurrent resolution of the external data I/O-bound rules depend on, a repository over PDO whose queries are specifications compiled to SQL (with lifecycle columns, batch and set-based operations and relationships loaded in batch), domain events emitted when an entity starts or stops satisfying a specification, on a write or by the mere passage of time, declared feature flags with targeting and an audit trail, text comparison that ignores case and accents with the same answer in memory and in SQL, and specifications written as canonical JSON and read back without unserialize() nor a class name taken from the input. Every promise of its algebra is checked against the evaluation by a soundness bench.

The documentation lives in docs/en (English) and docs/pt-BR (Portuguese, Brazil), one page per topic, in the Docs Kit format (a docs-nav.json per language). The examples keep the numbers they had in this README (example 17.1 is still ### 17.1., now on its page), and every PHP block of the pages is checked by the test suite.

⚡ First Use

A rule is an object: compose it, ask it whether a candidate satisfies it, get every failure with its code and message without an exception, and compile the same rule to SQL (the same block opens Getting Started):

use Antevemus\ASpecification\Spec;

// Two business rules, each with its own message and code
$adult = Spec::property('age', Spec::greaterThanOrEqualTo(18))
    ->because('Customer must be of legal age.')
    ->withCode('CLI_001');
$active = Spec::property('status', Spec::equalTo('ACTIVE'))
    ->because('Only active accounts can receive a credit line.')
    ->withCode('CLI_002');
$creditRule = $adult->and($active);

$customer = (object) ['age' => 17, 'status' => 'ACTIVE'];   // any object: an entity, a DTO, a stdClass

$creditRule->isSatisfiedBy($customer);                      // false

$result = $creditRule->evaluate($customer);                 // no exception: a result with every failure
count($result->failures);                                   // 1
$result->failures[0]->code;                                 // 'CLI_001'
$result->failures[0]->message;                              // 'Customer must be of legal age.'

// The same rule as a parameterized WHERE clause (15 drivers, 10 SQL dialects)
$where = Spec::toSql($creditRule, 'pgsql');
$where->getSql();         // '(("age" IS NOT NULL AND "age" >= :p1) AND ("status" IS NOT NULL AND "status" = :p2))'
$where->getParameters();  // [':p1' => 18, ':p2' => 'ACTIVE']

📋 Requirements

  • PHP: ^8.2 (tested on PHP 8.2 and 8.4). PHP 8.4 is required only by the optional ALinq integration (Antevemus.AlinqCollection, Module 14), which the library detects at runtime.
  • PHP Extensions:
    • ext-json (for JSON entity serialization; required)
    • ext-mbstring (required since v1.5.0: Unicode-aware case-insensitive string specifications, mb_strtolower)
    • ext-intl (optional: TextComparison::IgnoreCaseAndAccents, the text comparison that also ignores accents, examples 20 and 20.1; without it that option is refused with MissingExtensionException and everything else works)
    • ext-sysvsem (optional: SysVSemaphoreSynchronizer, the reader/writer lock shared between processes through SysV IPC semaphores, Linux/Unix)
    • ext-curl (optional: HttpDataSource, the concurrent HTTP fetching of the DataResolver, Module 21; without it an HTTP source is refused when registered and CallableDataSource keeps working)
    • ext-pdo (optional, with the driver of your database: PdoRepository (Modules 22, 25 and 26), the PDO stores of the state machine, PdoRuleCatalog and the PDO domain event outbox and trigger state store (Modules 23 and 24))
  • Dependency: one, from the same author, with no dependencies of its own: antevemus/adeep-property-access ^1.0 (since v2.0.0), the property accessor shared with ALinq, so that both libraries read the same value from the same candidate (example 10.1). Composer installs it.
  • Optional package: antevemus/alinq-collection ^2.2.0 for ALinqBridge (typed IALinqCollection/IALinqLazyCollection returns), ALinqSpecificationVisitor and O(1) lazy streaming over IRepository::iterate() (requires PHP 8.4; ALinq reads a non-public property only when it is marked #[Specifiable], the same rule as this package, and its 2.0 resolves properties through the same shared accessor).

Upgrading from 1.x? Read UPGRADE-2.0.md first: .bin files of the PHP-native serializer and rule catalogs with Portuguese values are converted on 1.14.x, before the upgrade.

🚀 Installation

Install via Composer: composer require antevemus/aspecification. Requirements, installation and the first use, in detail: Getting Started.

📚 Documentation

One line per page, grouped as in the navigation; the number in parentheses is the example of the former README that the page hosts.

Getting started

Concepts

Specifications

  • Fluent chaining (examples 1 and 1.1) · pt-BR: Business rules composed as natural-language sentences; int-typed leaves (asInteger(), Spec::int()).
  • Notification pattern (example 2) · pt-BR: evaluate(): every failure with its code, message and property, without exceptions.
  • Partial satisfaction (example 3) · pt-BR: remainderUnsatisfiedBy(): only the clauses a candidate still fails.
  • Tautology and simplification (examples 12 and 12.1) · pt-BR: isTautology(), isContradiction() and simplify().
  • Method calls (example 13) · pt-BR: MethodParameterizedSpecification: a rule that calls a method of the candidate.
  • Text comparison (examples 20 and 20.1) · pt-BR: Comparison ignoring case and accents, wildcards and regular expressions, in memory and in SQL.
  • Serialization (example 21) · pt-BR: SpecificationCodec: specifications as canonical JSON, read back without unserialize().

Repositories

  • Partitioned DAG (example 4) · pt-BR: Repositories partitioned by specifications, with early pruning of disjoint branches.
  • Persistence and cache (examples 5 and 5.1) · pt-BR: File repositories, the hybrid L1 (RAM) + L2 (disk) tier and its write-behind mode.
  • PDO repository (example 17) · pt-BR: PdoRepository: queries are specifications compiled to SQL.
  • Lifecycle (example 17.1) · pt-BR: Timestamps, soft delete, hooks and the SQL log.
  • Batch writes (example 17.2) · pt-BR: Batch writes and set-based operations.
  • Relationships (example 17.3) · pt-BR: References, compositions and eager loading in batch, never N+1.

Rule engine

  • Dynamic rules (example 7) · pt-BR: Rules compiled at runtime from relational catalogs, severity triage and the document matrix.
  • A real consumer (example 22) · pt-BR: Lookup-only rule types, the most specific rule of a type and target labels.

SQL

Integrations

  • ALinq (example 10) · pt-BR: The ALinq bridge, the LINQ predicate compiler and lazy streaming.
  • Attributes (example 11) · pt-BR: #[AssertSpec] and #[ValidateRule] on PHP 8 classes.
  • Adianti host (example 22.3) · pt-BR: Adianti screens as hosts: a joined transaction and a UseCaseResult on TMessage/TToast.

State machine

  • State machine (example 14) · pt-BR: Guards are specifications: parallel regions, JSON definitions, the runner and persisted deadlines.
  • Deadlines in business time (example 14.11) · pt-BR: business days, working hours and holidays; a pause that does not spend the weekend.
  • Use cases (example 15) · pt-BR: IUseCase, UseCaseRunner and the result shared with the machine.
  • A real consumer (example 23) · pt-BR: legacy state codes, definitions versioned in the database with drafts and publication, and tenants sharing one machine.
  • Adopting it in a running system (example 24) · pt-BR: importing processes under way, effect handlers that read the event, an action panel that tells "give the data" from "not yours" with one reason to show, wide JSON columns and batches with sendMany().

Data resolution

  • Concurrent resolution (examples 16 and 16.2) · pt-BR: Spec::external() and the DataResolver: external data fetched concurrently; the reason and the retry delay of a failed HTTP call.
  • Asynchronous sources (example 22.2) · pt-BR: AsyncDataSource: submit once, poll later.
  • Deferred sources (example 16.1) · pt-BR: IDeferredDataSource and IStageTransport: the hook through which a satellite package fetches the non-HTTP sources of a stage together.

Domain events

Feature flags

  • Feature flags (example 19) · pt-BR: Flags declared in code and switched in the database, with targeting and an audit trail.
  • Window and rollout (example 19.1) · pt-BR: The validity window and the percentage rollout of a flag.

Caching

  • Caching (examples 23 and 23.1) · pt-BR: The rule catalog, the feature flags and the routing clusters of a partitioned repository read through a shared PSR-16 cache.

Benchmarking

Quality

  • Tests · pt-BR: The runner report, the Domian parity suite and the soundness bench.
  • Guarantees · pt-BR: The five checks no release ships without.

Project

🧪 Quality

  • Runner: php tests/run_all.php, 39/39 suites, 45 803 assertions, zero regressions (the report: Tests).
  • PHPUnit: vendor/bin/phpunit, 719 tests and 119 661 assertions, the Domian parity suite (230 tests) included.
  • Soundness bench: every promise of the algebra checked against the evaluation over a pool of over 700 specifications, with a guard that fails when a specification class is missing from it.
  • SQL parity: the same answers in memory and in PostgreSQL 17, MySQL 8.4 and MariaDB 11.4, in CI.
  • Mutation testing: Infection with a floor that only rises, now 89 (90.54% MSI measured in 2.5.0).
  • The five permanent guarantees and how to run them: Guarantees.

🗺️ Roadmap & Next Steps

Milestones shipped and planned, in short: Roadmap. The full strategic plan: 👉 ROADMAP.md.

⚖️ Upstream Attribution, Licensing & Acknowledgments

Antevemus ASpecification expresses its deepest respect and gratitude to the pioneer authors who established the theoretical and practical foundations of this pattern:

  • Eirik Torske (Project Administrator & Developer) and Bjørn Nordlund (Contributor), creators of the Domian (Java) framework, whose pioneering work in Boolean algebra, Venn set calculus (isGeneralizationOf, isSpecialCaseOf, isDisjointWith), and partitioned repository architectures served as the inspiring foundation for this PHP 8.2+ implementation.
  • Eric Evans and Martin Fowler, for authoring the seminal paper Specifications (2002) and foundational works on Domain-Driven Design (DDD).

Apache License 2.0 Compliance

The upstream Domian framework is distributed under the terms of the Apache License, Version 2.0 (Copyright 2006-2010 the original author or authors, as stated in the Domian sources; developers Eirik Torske and Bjørn Nordlund). In full compliance with Section 4 of said license:

  • Original authorship and copyright notices are fully preserved.
  • The NOTICE.md and THIRD_PARTY_NOTICES.md files contain the formal provenance and full third-party license texts.
  • This independent PHP implementation is made available to the global open-source community under the MIT license. It is not a clean-room rewrite: it was written with Domian's public sources at hand, and the files that follow a specific Domian class or reuse a sentence of its documentation say so in their DocBlock, keeping the upstream attribution as Section 4 of the Apache License requires.

📄 License

Distributed under the MIT license. See LICENSE, NOTICE.md, and THIRD_PARTY_NOTICES.md for more information.

👨‍💻 Authors & Corporate Maintenance

Heliton Junior (CTO)