componenta / cqrs-app
Componenta application integration for CQRS discovery compilation
Requires
- php: ^8.4
- componenta/app: ^2.0 || ^3.0.2
- componenta/class-finder: ^1.0
- componenta/config: ^2.0.2
- componenta/cqrs: ^2.0.1 || ^3.0.3
- componenta/di: ^3.0 || ^4.0.2
- componenta/tokenizer: ^1.0
- psr/container: ^2.0
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
- componenta/cqrs-lock: Adds lock metadata and command locking.
- componenta/cqrs-policy: Adds command policy integration.
- componenta/cqrs-retry: Adds retry metadata and middleware.
- componenta/cqrs-transport: Adds async transport metadata and dispatch.
README
componenta/cqrs-app adds application-level discovery and build-time map compilation for componenta/cqrs.
It does not contain runtime middleware, transports, or console workers. Install the relevant optional CQRS packages for those concerns.
Installation
composer require componenta/cqrs-app
Dependencies
| Dependency | Purpose |
|---|---|
PHP ^8.4 |
Modern language features and strict types. |
componenta/app |
Application discovery and production build integration. |
componenta/class-finder |
Class discovery and listener compiler contracts. |
componenta/config |
Configuration and factory integration. |
componenta/cqrs |
Core CQRS runtime, map contracts, and config keys. |
componenta/tokenizer |
Supplies ClassInfo and its existing reflector to discovery. |
psr/container |
Service lookup. |
What It Registers
| Config section | Entries |
|---|---|
factories |
CqrsDiscoveryIndex, CqrsMapCompiler, and the application implementation of CqrsMapProviderInterface. |
ClassFinderConfigKey::LISTENERS |
The single CqrsDiscoveryIndex listener. |
CompileConfigKey::LISTENER_COMPILERS |
The compiler that emits ConfigKey::CQRS_MAP. |
AppConfigKey::AUTOWIRE_ENTRY_CONTRIBUTORS |
The discovery index that contributes discovered handlers and listeners to DI compilation. |
Usage
Register the core provider first and the application provider second:
return [ new Componenta\CQRS\ConfigProvider(), new Componenta\CQRS\App\ConfigProvider(), ];
componenta/cqrs provides the standalone CqrsMapProviderInterface binding backed by configured data. componenta/cqrs-app replaces that one binding with an application factory. In development the factory returns a composite of the configured and discovered maps; outside development it returns the configured provider reading the compiled map.
All core runtime consumers continue to depend on the same CqrsMapProviderInterface: command, query and listener locators, command metadata, and the compiler therefore observe one effective map.
Use discovery attributes in application code:
use Componenta\CQRS\Command\Attribute\AsCommandHandler; #[AsCommandHandler] final readonly class PublishPostHandler { public function __invoke(PublishPostCommand $command): void { // Handle command. } }
Discovery And Metadata
CqrsDiscoveryIndex reads ClassInfo::$reflector once per class and collects command handlers, query handlers, listeners, known command names, and configured command metadata attributes. It validates public non-static handler methods, rejects conflicting handlers, deduplicates identical listeners, and performs deterministic sorting in finalize().
Optional packages add metadata without changing the compiler by appending an attribute class to ConfigKey::COMMAND_METADATA_ATTRIBUTES. The factory validates that every entry exists and is declared with #[Attribute].
ConfigKey::DISCOVERY_ENABLED may explicitly enable or disable the live overlay. When omitted, live discovery is enabled only for exact APP_ENV=development. Every other environment requires a compiled CQRS map and rejects an attempt to enable runtime discovery.
Production Build
With componenta/app-console, build the artifact before starting production:
APP_ENV=development php bin/console.php app:build
The application provider first produces the same effective map used by development dispatch: configured map plus discovery map, merged through CqrsMap::merge(). CqrsMapCompiler serializes that effective map as one deterministic versioned artifact. The build merge replaces numeric descriptor positions instead of appending the configured portion a second time.
Production reads the resulting complete artifact through the same CqrsMapProviderInterface, without scanning application classes. Metadata for a known compiled command never falls back to reflection; an unknown command may still use the reflection provider.
An old CQRS key, unsupported map version, or missing non-development map fails with an instruction to clear caches and rebuild. After upgrading from v1, remove the config, discovery, old CQRS, and legacy container caches before running app:build.
Optional Runtime Packages
Install separate packages for runtime concerns:
| Package | Adds |
|---|---|
componenta/cqrs-policy |
Command/query policy middleware. |
componenta/cqrs-retry |
Retry middleware. |
componenta/cqrs-lock |
Resource lock middleware. |
componenta/cqrs-transaction-cycle |
Cycle Database transaction middleware. |
componenta/cqrs-transport |
Async transport middleware, contracts, serializer, and worker. |
componenta/cqrs-transport-cycle |
Cycle Database transport implementation. |
componenta/cqrs-transport-console |
cqrs:worker Symfony Console command. |