Download the PHP package alexandrebulete/ddd-outbox-bundle without Composer
On this page you can find all versions of the php package alexandrebulete/ddd-outbox-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download alexandrebulete/ddd-outbox-bundle
More information about alexandrebulete/ddd-outbox-bundle
Files in alexandrebulete/ddd-outbox-bundle
Package ddd-outbox-bundle
Short Description Asynchronous effects you can trust in a DDD application: sent if and only if the action commits, tracked one by one, retried from the back office — on the Messenger Doctrine transport, without an extra outbox table.
License MIT
Informations about the package ddd-outbox-bundle
DDD Outbox Bundle
Asynchronous effects you can trust: nothing lost, nothing phantom, nothing twice — and every one of them followed, explained and retried on its own.
There is no outbox table in this bundle. On the Messenger Doctrine transport, a message sent while an action is handled is inserted in that action's transaction: it leaves if and only if the action commits. The transport is the outbox. What this bundle adds is what makes it safe to rely on: a guard against the one way to break that property, a follow-up of every message, a back-office screen and a retry.
If you come from a hand-rolled outbox (a table plus a relay every minute): there is no relay, no minute of latency — PostgreSQL's LISTEN/NOTIFY wakes the worker on insert — and one reaction is one message, retried on its own.
Install
Requires alexandrebulete/ddd-symfony-bundle ≥ 1.5, whose tracing gives each
message its id, its actor and its chain.
The promise holds on these conditions — check them once:
- Doctrine transport, same connection as the entities. Another transport (Redis, AMQP) cannot join the database transaction: the guarantee is gone.
- A failure transport: that is where failed jobs wait to be retried.
- On PostgreSQL, keep
use_notify(on by default): it is what makes the latency milliseconds rather than a polling interval. A delayed message — an automatic retry — is not notified when it becomes due: the worker looks for them everycheck_delayed_interval(60 000 ms by default). Lower it on the transport if retries must be quicker.
Writing an asynchronous effect
One reaction with an effect, one message — never a domain event fanned out to N asynchronous subscribers: each effect then has its own retries, its own failure, its own retry by hand.
The handler must be idempotent (at-least-once delivery): derive a key from what the message is about — (mission, step) — and do nothing if it is done.
Never DispatchAfterCurrentBusStamp for an asynchronous message: it would
leave after the commit, and be lost if the process stops in between. The
bundle refuses it with a LogicException.
What is guaranteed
| How | |
|---|---|
| Sent if and only if the action commits | the Doctrine transport inserts in the action's transaction; DispatchAfterCurrentBusStamp refused |
| A failure does not freeze the queue | each message is retried on its own, then parked in the failure transport; a message that closes the EntityManager does not take the next ones down (the worker resets it between messages) |
| Every message followed | a job per message: queued → running → succeeded | retrying → failed |
A job is written in the same transaction as its message: it exists if and
only if the message does. It carries the message, the transport, who is
behind it, its chain (correlation_id, causation_id — the activity journal
of ddd-activity-bundle shares them), its attempts and last error.
Only messages leaving for a real transport are followed; sync is a function
call.
Back office
With Sylius Admin UI, "Background jobs" lists jobs with filters (status, job,
chain): what is stuck, started by whom, since when, why. A failed job has a
Retry button: RetryJobCommand, a use case like any other — a permission
(outbox.retry_job), a line in the journal. The message goes back on its
transport with a fresh set of attempts, still on behalf of whoever started it.
Retention
Failed jobs are kept: they still wait for someone.
Configuration
Development
The integration tests run a real worker on real Doctrine transports, on the
database given by DDD_TEST_DATABASE_URL (a SQLite file otherwise); CI runs
them on PostgreSQL, MySQL and SQLite.
All versions of ddd-outbox-bundle with dependencies
alexandrebulete/ddd-doctrine-bridge Version ^1.4
alexandrebulete/ddd-foundation Version ^1.5
alexandrebulete/ddd-sylius-bundle Version ^1.0
alexandrebulete/ddd-symfony-bundle Version ^1.5
doctrine/dbal Version ^4.3
doctrine/doctrine-bundle Version ^2.13 || ^3.0
doctrine/doctrine-migrations-bundle Version ^3.7 || ^4.0
doctrine/migrations Version ^3.8
doctrine/orm Version ^3.3
pagerfanta/core Version ^4.0
psr/clock Version ^1.0
psr/log Version ^3.0
sylius/admin-ui Version >=0.10
sylius/grid-bundle Version ^1.13
sylius/resource-bundle Version >=1.14
symfony/clock Version ^7.0 || ^8.0
symfony/config Version ^7.0 || ^8.0
symfony/console Version ^7.0 || ^8.0
symfony/dependency-injection Version ^7.0 || ^8.0
symfony/doctrine-bridge Version ^7.0 || ^8.0
symfony/doctrine-messenger Version ^7.0 || ^8.0
symfony/framework-bundle Version ^7.0 || ^8.0
symfony/http-foundation Version ^7.0 || ^8.0
symfony/messenger Version ^7.0 || ^8.0
symfony/routing Version ^7.0 || ^8.0
symfony/security-csrf Version ^7.0 || ^8.0
symfony/translation Version ^7.0 || ^8.0
symfony/uid Version ^7.0 || ^8.0