Download the PHP package kraz/messenger-workflow without Composer

On this page you can find all versions of the php package kraz/messenger-workflow. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package messenger-workflow

MessengerWorkflow

[!WARNING] This Symfony bundle is a proof of concept. Using it in production is not recommended!

A Symfony bundle implementing Enterprise Integration Patterns on top of Symfony Messenger: CQRS message buses, transactional outbox/inbox, tracked async tasks and RabbitMQ integration. It is the messaging backbone for a modular monolith whose bounded contexts (modules) communicate only asynchronously — so they can later be split into independently deployed applications without touching application code.

Upgrading from 0.2.x? Read UPGRADE-0.3.md.

Message model

Every message goes through the message broker — there is no synchronous in-process handling, so behavior is identical whether the handler lives in the same deployment or in another one.

Flows

Segments in [...] are optional per configuration — see Reducing the flow. marks the at-least-once hand-offs: a failed hand-off keeps the message on the safe side.

Every flow variant is traced end to end — configuration, derived workers, the exact worker commands and what happens at each hop — in MESSAGE_FLOWS.md.

Features

Installation

Requires PHP ≥ 8.4, Symfony ^8.1, ext-redis, PostgreSQL, RabbitMQ and Redis. Register the bundles:

Configuration

The bundle prepends the three buses (command.bus, query.bus, event.bus), the broker transports (commands, queries, events — you supply the DSNs), the marker-interface routing and the serializer defaults. A typical bounded context ("book_store", own database) adds:

Provision the broker topology and the database tables once per deploy:

The full configuration shape (all keys, defaults and descriptions) is generated into config/reference.php.

Full default configuration

All keys of the messenger_workflow extension with their default values and inlined comments (cross-check anytime with bin/console config:dump-reference messenger_workflow):

Transport DSN options

Option Transports Default Meaning
table_name outbox, inbox, failures zz_commands_* / zz_events_* storage table (inbox dedup index table = <table_name>_index)
multiple_consumers inbox commands true, events false competing consumers via FOR UPDATE SKIP LOCKED
strict_order inbox, outbox false request single-consumer FIFO; conflicts with multiple_consumers (boot-time error)
transactional_handler inbox commands true, events false run handlers inside the inbox-row-deleting transaction
redeliver_timeout inbox, outbox 300 seconds before an unacked in-flight row is redelivered
get_notify_timeout, check_delayed_interval inbox, outbox (PostgreSQL) 60000 LISTEN/NOTIFY wait and re-poll interval (ms)
queue_name failures scopes the stock Doctrine failure transport per queue

Usage

Messages are plain classes implementing the marker interfaces Application\CommandInterface, Application\QueryInterface or Domain\DomainEventInterface. Handlers:

The attributes also work on methods, and a handler method may declare extra parameters after the message — they are resolved from the container with full autowiring semantics (#[Autowire]/#[Target] included; nullable parameters degrade to null when no service matches, defaults are kept). This lets a controller class host its feature's handler without constructor injection:

(Batch handlers keep Symfony's native ($message, Acknowledger) signature and are never wrapped.)

Domain events are published through the context's outbox bus (atomic with the domain transaction) or directly to the event bus (immediate, less resilient):

Task status and results (e.g. for a polling HTTP endpoint), with tasks.enabled: true:

Workers

Every flow segment is executed by messenger:consume workers under supervisord. The worker set is derived automatically from the transport topology — for the configuration above: an events publisher (relay), receiver + handler pairs for the commands and events queues, a commands notifier and a query handler. Inspect/override under messenger_workflow.messenger.workflow:

Manual entries merge with the derived set by name or type|source|queue identity; enabled: false removes a worker; labels carries free-form metadata. cmd_extra_options.keepalive renders messenger:consume --keepalive=<s>, protecting a long-running handler on a competing-consumer source from mid-flight redelivery (keep it below the transport's redeliver_timeout; single-consumer sources ignore the in-flight marker entirely). instances > 1 requires a competing-consumer source — on a single-consumer transport the container fails at compile time, because every process would handle the same messages.

cmd_extra_options.sleep renders messenger:consume --sleep=<s>, the idle pause between polls. The derived defaults are already tuned per transport kind and rarely need overriding: broker-queue workers (receivers, query handlers, no-inbox handlers) get sleep: 0 because the AMQP consumer blocks on the broker socket — no polling loop exists to pace. PostgreSQL-backed workers (publishers, notifiers, inbox handlers) keep the messenger default (1 s), but their idle pacing really comes from LISTEN/NOTIFY: the worker blocks query-free until a notification or the get_notify_timeout wake-up, so lowering sleep there buys nothing. One combination to avoid: sleep: 0 on a worker that consumes a PostgreSQL transport together with another transport in one messenger:consume — with mixed transports the NOTIFY wait is capped to the worker's sleep to keep the sibling transport polled, and a zero cap disables the wait entirely, leaving an unpaced loop of empty polls against the database. Keep the derived one-transport-per-worker shape and sleep: 0 stays where it belongs — on AMQP-only workers.

Generate the supervisord config:

Reducing the flow

The optional segments can be removed per context — each removal is an informed trade-off:

Removed Consequence
outbox The bus publishes straight to AMQP. Dispatch and database commit are separate writes (dual-write risk); broker downtime surfaces at dispatch time.
inbox The handler worker consumes the broker queue directly (--queues=<q>). No deduplication — handlers must be idempotent; per-queue fromTransport scoping is unavailable. Map the queue in orm_mappings to keep a plain middleware transaction around handlers. The <queue>_notifier convention still works.
notifier Tracked command results are written to the result storage directly from the handler worker (a dual write outside the handler transaction). Untracked commands never used the notifier.

All reductions are exercised by the integration suite (tests/Integration/Flow/ReducedFlowTest.php) and walked through hop by hop in MESSAGE_FLOWS.md.

Ordered event delivery

Events are FIFO end-to-end when every segment has a single consumer: the outbox relay is single-consumer by design, and the events inbox defaults to single-consumer FIFO. A retrying (poison) message blocks its ordered queue only for its bounded retry budget, then drains to the failure transport and the queue resumes. Declaring strict_order=true on a transport documents the intent and makes conflicting multiple_consumers configuration fail at boot (container compile time).

Retry backoff delays apply on the inbox hop too: a retry redelivery stamps the row with an available_at. In single-consumer FIFO mode a message in backoff blocks its successors (ordering is preserved — the queue waits, bounded by the flow's total retry budget); in competing-consumer mode the row is simply skipped until due. On PostgreSQL a worker sleeping on LISTEN/NOTIFY picks a due retry up at the next idle wake-up — governed by get_notify_timeout (default 60 s) with the idle listener active (the normal bundle mode), or by the check_delayed_interval re-poll (default 60 s) when the transport is wired standalone without the listener. Lower the matching interval on transports where precise backoff timing matters.

The unit of work at the transaction boundary

WorkflowTransactionMiddleware opens the transaction around a received message — and closes the unit of work inside it. Before committing, every open ORM entity manager running on that transaction's connection is flushed, so an ORM application gets a real unit of work per message:

A handler that flushes itself is unaffected — the boundary flush then finds nothing to do. The write happens before the inbox row is removed, so a failing flush leaves the message in the inbox and the whole transaction rolls back: the message is retried, never silently lost.

Managers are selected by connection name, resolved at container compile time: the inbox transport and the entity managers both name their connection in configuration and resolve the same doctrine.dbal.<name>_connection service, so the compiled connection name → entity managers map is exactly the set of managers taking part in this message's transaction. Per message that is a single hash lookup — with dozens of bounded contexts, the other contexts' managers are never instantiated or scanned. Other bounded contexts, and managers running their own transactions (a projection writer, say), are untouched.

Two guards catch the misconfiguration this map cannot repair — handlers writing through an entity manager on a different connection than the message transaction (such writes could never be atomic with the inbox-row removal):

Only received messages are wrapped, which is also what makes the flush safe to do here: a sender-side dispatch() carries no ReceivedStamp and returns before reaching the flush — including the outbox's own dispatch, which happens from inside a flush and would otherwise re-enter one.

Turn it off to leave writing entirely to the application:

Failure transports (DLQ) and replays

Inbox deduplication covers broker redelivery only: a message UUID already recorded as processed is dropped when RabbitMQ delivers it again. An operator replay (messenger:failed:retry) deliberately bypasses that record and re-executes the handler — that is the escape hatch for the crash window where a message was marked processed but its side effects were lost (e.g. a non-transactional handler crashing between the application write and the inbox ack). The operational consequences:

Why replays do not consult the dedup index (decision, 0.4): the index marks a message UUID processed on both terminal outcomes — successful ack and permanent rejection — so every message sitting in a failure transport is already marked processed, and a dedup check on replay would block all replays. More fundamentally, no marker can distinguish "failed before applying its side effects" from "failed after": whether a replay is safe is a property of the handler, not of the message's delivery history. Idempotent handlers and transactional_handler=true are the supported mechanisms; the bypass is deliberate and will stay.

Testing

The PHPUnit suite (unit + functional + integration) expects live local infrastructure for the integration groups:

Connection overrides: MWF_TEST_PG_*, MWF_TEST_REDIS_*, MWF_TEST_AMQP_DSN (defaults in phpunit.xml.dist).

License

This library is licensed under the MIT License. See the LICENSE file for details.


All versions of messenger-workflow with dependencies

PHP Build Version
Package Version
Requires php Version >=8.4
ext-redis Version *
doctrine/orm Version ^3.6
symfony/console Version ^8.1
symfony/doctrine-messenger Version ^8.1
symfony/messenger Version ^8.1
symfony/serializer Version ^8.1
symfony/uid Version ^8.1
symfony/yaml Version ^8.1
webmozart/assert Version ^2.4
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package kraz/messenger-workflow contains the following files

Loading the files please wait ...