antevemus / aspecification
Enterprise Specification Pattern Framework for PHP 8.2+ (DDD, Notification Pattern, SQL Query Visitor, Adianti TCriteria, DAG Repositories)
Requires
- php: ^8.2
- ext-json: *
- ext-mbstring: *
- antevemus/adeep-property-access: ^1.0.0
Requires (Dev)
- infection/infection: ^0.31.9 || ^0.35.6
- phpunit/phpunit: ^11.0
- psr/simple-cache: ^3.0
Suggests
- ext-curl: HttpDataSource: concurrent data resolution over HTTP through curl_multi (DataResolver, Module 21); without it an HttpDataSource is refused when registered and CallableDataSource keeps working
- ext-intl: TextComparison::IgnoreCaseAndAccents and Spec::equalIgnoreCaseAndAccents(): text compared ignoring case and accents (Normalizer: NFD without the nonspacing marks); without it those specifications are refused when built (MissingExtensionException) and every other text operator keeps working
- ext-pdo: PdoRepository (repository over PDO: specifications compiled to SQL, attribute or builder mapping), the PDO persistence of the state machine and the PDO domain-event outbox
- ext-sysvsem: SysVSemaphoreSynchronizer: read/write lock shared between processes through SysV IPC semaphores (SemaphoreSynchronizer is intra-process)
- antevemus/alinq-collection: ^2.2.0 — ALinqBridge (typed IALinqCollection/IALinqLazyCollection returns), ALinqSpecificationVisitor and lazy streaming over IRepository::iterate() (Module 14); requires PHP 8.4; reads a non-public property only when marked #[Specifiable], as this package does (2.x resolves properties through the same shared accessor, antevemus/adeep-property-access)
- antevemus/aspecification-revolt: ^1.0 — RevoltDataSource and RevoltStageTransport: the non-HTTP sources of a resolution stage fetched concurrently under the Revolt event loop (Fibers, Amp futures) through the IDeferredDataSource/IStageTransport hook of 2.6.0
- psr/simple-cache: ^1.0 || ^2.0 || ^3.0 — CachedRuleCatalog, CachedFeatureFlagStore and PartitionRepository::withIndexCache() (2.8.0): the rule catalog, the feature flags and the routing clusters of a partitioned repository read through a PSR-16 cache shared between servers, with an implementation of your choice (Redis, Memcached, APCu); without it those classes cannot be built and nothing else changes
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v2.12.0
- v2.11.1
- v2.11.0
- v2.10.0
- v2.9.0
- v2.8.0
- v2.7.0
- v2.6.1
- v2.6.0
- v2.5.2
- v2.5.1
- v2.5.0
- v2.4.0
- v2.3.0
- v2.2.0
- v2.1.1
- v2.1.0
- v2.0.0
- v1.14.0
- v1.13.0
- v1.12.0
- v1.11.0
- v1.10.0
- v1.9.0
- v1.8.1
- v1.8.0
- v1.7.0
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.4
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.0
This package is auto-updated.
Last update: 2026-11-02 01:00:04 UTC
README
🇺🇸 English | 🇧🇷 Português (Brasil)
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 withoutunserialize()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 withMissingExtensionExceptionand 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 theDataResolver, Module 21; without it an HTTP source is refused when registered andCallableDataSourcekeeps working)ext-pdo(optional, with the driver of your database:PdoRepository(Modules 22, 25 and 26), the PDO stores of the state machine,PdoRuleCatalogand 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.0forALinqBridge(typedIALinqCollection/IALinqLazyCollectionreturns),ALinqSpecificationVisitorand O(1) lazy streaming overIRepository::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:
.binfiles 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
- Installation and first use · pt-BR: Requirements, installation and a first rule evaluated and compiled to SQL.
Concepts
- Origins and foundation · pt-BR: The Evans & Fowler paper, the Domian (Java) origin and the references.
- Core features · pt-BR: Every capability of the library in one list, with the module and the example of each.
- What sets us apart · pt-BR: A comparison with Domian and with typical PHP specification libraries.
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()andsimplify(). - 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 withoutunserialize().
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
- SQL query visitor (example 8) · pt-BR: Parameterized
WHEREclauses for 15 drivers and 10 SQL dialects. - TCriteria builder (example 9) · pt-BR: Specifications compiled to the
TCriteriaandTFilterof Adianti. - TCriteria: accents and subqueries (example 22.1) · pt-BR:
withAccentFolding(),inSubquery()andexistsIn().
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
UseCaseResultonTMessage/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,UseCaseRunnerand 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 theDataResolver: 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:
IDeferredDataSourceandIStageTransport: the hook through which a satellite package fetches the non-HTTP sources of a stage together.
Domain events
- From specifications (example 18) · pt-BR: Domain events when an entity starts or stops satisfying a specification.
- From the passage of time (example 18.1) · pt-BR:
SpecificationTrigger::timed()and theTriggerSweeper.
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
- Benchmarking (example 6) · pt-BR:
StopWatchandInstrumentationUtils.
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
- Directory structure · pt-BR: The
src/tree, folder by folder. - Roadmap · pt-BR: Milestones shipped and planned; the full plan is in ROADMAP.md.
- Attribution and license · pt-BR: Domian attribution, Apache 2.0 compliance and the MIT license.
- Authors · pt-BR: Maintainer and contact.
🧪 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)
- Email: contato@antevemus.com.br
- Website: antevemus.com.br
- Organization: Antevemus Soluções Inovadoras em TI Ltda.