trollbus/symfony-messenger-bridge
A bridge between Trollbus and Symfony Messenger that lets Trollbus applications reuse Messenger messages, handlers, middleware, and transports, simplifying interoperability and gradual migration between the two messaging systems.
This is a 0.1.0 release — the first public tag. There is no prior assessment and no migration path to worry about, but also no battle-testing: treat the public API as unstable until 1.0 and pin the exact version in composer.json ("trollbus/symfony-messenger-bridge": "0.1.*").
Install and publish:
composer require trollbus/symfony-messenger-bridge:^0.1.0
php artisan vendor:publish --tag=messenger-config # verify the tag in the service provider
Where to look first, in this order:
composer.json — confirm which symfony/messenger major it requires and whether symfony/messenger is a hard dependency or a suggest. This determines whether you're buying into the full Symfony component or a thin wrapper.src/*ServiceProvider.php) — check what it binds into the container: a MessageBusInterface, a RoutableMessageBus, an event dispatcher adapter, and whether it auto-registers middleware. This is the real API surface.config/messenger.php (if published) — default_bus, buses, transports, routing, failure_transport. This mirrors Symfony's messenger.yaml; map it mentally onto the Laravel config cache.First use case to validate end-to-end: dispatch a trivial command to the sync transport and assert the handler ran, then switch the same message to a real transport (database or Redis) and confirm messenger:consume-style consumption works. Doing the sync case first isolates wiring bugs from transport bugs.
Command/query separation via multiple buses. The typical setup is a command.bus and a query.bus with different middleware stacks — commands get transaction wrapping and retries, queries get nothing. Configure them under buses in config, then inject by name:
public function __construct(
#[Target('command.bus')] private MessageBusInterface $commandBus,
) {}
If the package's container binding doesn't support attribute targeting, fall back to contextual binding in a service provider.
Handler registration. Two patterns are common with bridges: explicit messenger.handlers config mapping message class → handler service, or Laravel-style auto-discovery via a #[AsMessageHandler]-equivalent attribute/tag. Prefer the explicit mapping in 0.1.x — auto-discovery in a young package tends to miss handlers in vendor packages and cached config.
Routing vs. dispatch() with a transport argument. Put durable routing in config (routing: [SendInvoice::class => 'async']) so it survives refactors and is visible in one place; reserve $bus->dispatch($msg, [new DelayStamp(60)])-style stamps for per-dispatch concerns (delay, priority). Mixing the two — stamping a transport that also has a config route — produces confusing double-sends.
Transaction safety. If you dispatch inside a DB transaction, wrap the dispatch in the bridge's transaction middleware (or dispatch after commit). With the database transport this matters most: the worker can pick up the message before the transaction commits and read stale/no data.
Failure handling. Configure a failure_transport and treat it as a real operational queue, not a bin — you'll want a messenger:failed:show / retry equivalent wired into your ops runbook, and an alert on failure-transport depth. Retries are stamp-based (RetryStrategy); per-message overrides go on the message class, not in the dispatch call, if you want them to survive retries.
Scheduling and workers. Run consumers as long-lived processes under Supervisor/Horizon-adjacent tooling, not as cron --once jobs. In local dev, a --limit/--time-limit flag is your friend to avoid zombie workers after deploys.
Testing. Use the sync transport in phpunit.xml env so handlers execute inline and assertions are trivial; keep one integration test per transport that actually asserts a message round-trips through the real broker.
config(), php artisan config:cache can silently deserialize stale transports. Verify with config:cache then config:clear in staging before deploy.ContainerInterface with Laravel's. If a handler is registered in Symfony's handler locator but not resolved from Laravel's container, you get "no handler for message" errors that look like routing bugs. Check both.dispatch_after_current_bus and send_message middleware must sit at the right ends or messages get double-dispatched or never sent.php artisan queue:restart-equivalent (or a file-touch sentinel) to your deploy script, or schedule the consumer with --time-limit.MessageBus decorator binding, a middleware registration hook, and an event dispatched on send/fail. If any of those are absent, file an issue now while the maintainer is still shaping the API.How can I help you explore Laravel packages today?