Download the PHP package elriseio/application-layer-bundle without Composer
On this page you can find all versions of the php package elriseio/application-layer-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download elriseio/application-layer-bundle
More information about elriseio/application-layer-bundle
Files in elriseio/application-layer-bundle
Package application-layer-bundle
Short Description Emphasizes that this is the Application Layer, not tied to the API specifics or any particular domain.
License MIT
Homepage https://github.com/elriseio/application-layer-bundle
Informations about the package application-layer-bundle
AppLayerBundle
Further reading
- CQRS application layer for API Platform — the architectural rationale behind this bundle: separating the transport, application, and domain layers in a CQRS-shaped Symfony system and mapping the boundary onto API Platform state providers and processors.
- Application Layer Bundle project page — overview, supported features, installation notes, and release notes.
- demo_application_layer — a runnable reference implementation of the bundle, showing how DTOs, command/query handlers, controllers, and the API Platform state provider/processor wiring fit together in a real Symfony application.
Symfony bundle that implements the application boundary of a CQRS-shaped
DDD system. It carries the HTTP request through sanitization → DTO
denormalization → command or query handler invocation → optional queue
dispatch, with first-class interfaces for CommandHandlerInterface and
QueryHandlerInterface.
The bundle is transport-agnostic: it works in plain Symfony controllers,
under API Platform state providers and processors, in Messenger
handlers, or in console commands. It depends only on
symfony/serializer and the standard Symfony service container, with
symfony/messenger as an optional integration for async commands.
Why it exists
A typical Symfony HTTP handler conflates three concerns:
- request parsing and validation (transport),
- mapping the validated payload into a use-case input (application layer),
- orchestrating the use-case against the domain model (application/domain).
AppLayerBundle claims the middle slice. The boundary between
transport and use-case is an immutable DTO. The use-case itself is
expressed as a CommandHandler (mutates state, returns a result) or a
QueryHandler (returns read-side data). The handler can use API
Platform, Messenger, custom repositories, or anything else — the
bundle imposes no constraint beyond the contract.
This shape keeps the use-case unit-testable in isolation, makes the intent of every endpoint explicit (command or query), and lets the transport layer (controllers, API Platform, RPC, CLI) stay a thin adapter.
Key features
- CQRS-shaped contracts: distinct
CommandHandlerInterfaceandQueryHandlerInterface, registered through separate tagged locators. - Immutable DTO denormalization through
symfony/serializer, with built-in support for readonly constructor-promoted DTOs and property-only DTOs (nosetAccessiblesince PHP 8.5). - Optional request sanitization for command payloads. Queries skip sanitization by design — read-side input is not mutated.
- Synchronous and asynchronous command handling via
symfony/messenger. TheMessengerQueueDispatcheris wired automatically when the package is installed; otherwise aNullQueueDispatcheris used. - Pluggable processor pipeline (
DataProcessorInterface) for endpoints that don't carry a DTO (lookup tables, projections, etc.). - Structured error handling through
RequestException, which wraps every denormalization, locator, and handler-resolution failure with diagnostic context.
Architecture
Contracts
CommandHandlerInterface— taggedapp_layer.command_handler. Mutates state and returns a command result (id, presenter, view DTO).QueryHandlerInterface— taggedapp_layer.query_handler. Returns read-side data without side effects.DataProcessorInterface— taggedapp_layer.data_processor. Used for endpoints without a DTO.DtoDeserializerInterface— abstraction over the underlying denormalizer. Default:SymfonyDtoDeserializer.RequestToDtoConverterInterface— extracts the payload from a SymfonyRequestand turns it into a DTO.RequestSanitizerInterface— optional pre-DTO cleanup for commands.
Components
DtoRequestHandler— orchestrates the pipeline above. Two entry points:dispatchCommand()anddispatchQuery().DefaultRequestToDtoConverter— JSON body or query/form merger, then DTO denormalization.SymfonyDtoDeserializer— routes DTOs with constructors toObjectNormalizer; handles property-only DTOs via direct reflection (nosetAccessible).DataProcessor— tagged locator for processor-only endpoints.
Dispatchers
DtoQueueDispatcherInterface— the abstract dispatch boundary.MessengerQueueDispatcher— implemented whensymfony/messengeris installed.NullQueueDispatcher— fallback when no transport is installed.
Installation
Requirements
- PHP 8.3 or higher with the
ctype,curl, andjsonextensions enabled (all three are bundled by default in standard PHP distributions; they are listed incomposer.jsonrequirefor runtime-declaration clarity). - Symfony 7.2 or higher
Register the bundle:
Usage
1. Define an immutable DTO
2. Implement a command handler
3. Implement a query handler
4. Dispatch from a controller
To dispatch asynchronously, pass dispatchToQueue: true to
dispatchCommand. The handler still runs synchronously, and the
already-mutating command is also handed to the configured queue
dispatcher (Messenger, by default) for downstream consumers.
5. Use the processor pipeline (DTO-less endpoints)
API Platform integration
API Platform's Processor and Provider interfaces map naturally onto
DtoRequestHandler. The recommended pattern is to keep the API
Platform entity/state purely as a transport adapter and delegate to
the application layer for the actual use-case.
State Provider for read endpoints
Processor for write endpoints
The boundary stays explicit: API Platform owns OpenAPI, content negotiation, rate limits, and the response shape. The application layer owns the use-case. DDD aggregates, repositories, and domain services live one layer below, called only from the command/query handlers.
Testing
The bundle ships with PHPUnit coverage for the pipeline. Run:
Changelog
See CHANGELOG.md for release history.
Development
The bundle ships with a project-local pre-commit hook that runs
composer check (cs:check + test) so style drift and test
breakage are caught locally before push. The hook is wired through
core.hooksPath, so it only takes effect inside this checkout.
Install the hook once after cloning:
This sets core.hooksPath to ./.githooks. The hook then runs
automatically before every commit; bypass it with git commit --no-verify
when a commit legitimately needs to land without a re-run (for example,
a composer.lock rotation triggered by a maintainer-only action).
composer install does not auto-install the hook on purpose: CI must
not be polluted by git config calls, and the operator may prefer
their own tooling (Lefthook, Husky) over the bundled bash hook.
License
MIT. See LICENSE.
All versions of application-layer-bundle with dependencies
ext-ctype Version *
ext-curl Version *
ext-json Version *
doctrine/annotations Version ^2.0
symfony/config Version ^7.2
symfony/dependency-injection Version ^7.2
symfony/http-client Version ^7.2
symfony/http-foundation Version ^7.2
symfony/http-kernel Version ^7.2
symfony/intl Version ^7.2
symfony/polyfill-intl-icu Version ^1.31
symfony/property-access Version ^7.2
symfony/serializer Version ^7.2
symfony/validator Version ^7.2
symfony/yaml Version ^7.2