halloverden/symfony-messenger-queue-notifications-bundle
Symfony bundle for sending notifications through Messenger queues. Dispatch notification messages asynchronously and process them with workers via your configured transports, keeping your application responsive. Simple integration with Symfony Messenger for queued notification delivery.
First, a framework reality check. This is a Symfony bundle, not a Laravel package. There is no config/app.php provider registration, no php artisan vendor:publish, and no Notifiable trait magic. If you're a Laravel developer, you'll be reading Symfony docs alongside Laravel ones. The mental model maps surprisingly well though: Symfony Messenger ≈ Laravel Queues, Symfony Notifier ≈ Laravel Notifications, and the "queue" transport ≈ your Redis/database queue driver.
Minimal steps:
composer require halloverden/symfony-messenger-queue-notifications-bundleconfig/bundles.php:
HalloVerden\SymfonyMessengerQueueNotificationsBundle\...::class => ['all' => true],
symfony/messenger and symfony/notifier are installed and a transport is configured (MESSENGER_TRANSPORT_DSN=doctrine://default or redis://...).php bin/console messenger:consume async -vvWhere to look first: the bundle's DependencyInjection/Configuration.php (if present) will document every config key it exposes. That file is the fastest way to know what the bundle actually wires up versus what it just leaves to Symfony's defaults. Then skim the transport message class names — those tell you exactly what gets serialized onto the queue.
First real use case: password reset / email verification emails. These are the classic "must be async, must retry, must not block the request" notifications — a perfect smoke test for whether the package's queue plumbing works end-to-end.
Notification-as-message. The core pattern these bundles implement is: a notification is created, the bundle wraps it in a Messenger envelope, dispatches it to a transport, and a handler on the consumer side calls the underlying Notifier channel (email, SMS, chat). Your code sends the notification as usual; the bundle intercepts and defers.
Routing configuration. Symfony routes messages by class name via config/packages/messenger.yaml:
framework:
messenger:
routing:
'Symfony\Component\Notifier\Message\ChatMessage': async
'Symfony\Component\Notifier\Message\SmsMessage': async
'Symfony\Component\Notifier\Message\EmailMessage': async
If the bundle ships its own message wrappers, route those classes instead. This is the main integration knob.
Per-channel transports. A common pattern is separate transports for email_async and sms_async so you can scale consumers independently — SMS rate limits are more sensitive than email. If this package respects that, you get a big win; if it hardcodes one transport, you'll need to layer routing yourself.
Programmatic dispatch pattern. Rather than relying on the notifier's channel detection, call the notifier directly and let the bundle's bus dispatch happen:
$this->notifier->send(new ResetPasswordNotification($user), $user);
Keep the notification class itself free of queue-specific code — the bundle should handle serialization. If it requires an interface or a base class, that's the smell to watch for.
Retry + failure transport. Wire a failure transport (failure_transport: failed) and a retry strategy on the async transport. For notifications, retries should be limited (3–5 with exponential backoff) because a stale password reset email is worse than none. Use messenger:failed:retry sparingly.
Testing pattern. In tests, use messenger.transport.in_memory (or the sync transport) so notifications dispatch inline. Assert against the transport's message count rather than mocking the notifier — this validates the queue wiring, which is the whole point of the package.
Zero adoption = zero safety net. 0 stars and 0 dependents means no Stack Overflow answers, no GitHub issues to search, and no community-tested edge cases. Read the source before depending on it. If the handler is under 100 lines, budget an afternoon to understand it fully — and consider whether you could replicate it in your own src/ in less time than adopting it.
Serialization is the #1 failure mode. Symfony Messenger serializes messages onto the transport. Notification objects that hold live resources (an entity with relations, a service, a closure) will fail on serialize or, worse, serialize a stale snapshot. Keep notification payloads to scalars and IDs. If the bundle doesn't guard against this, add __serialize/__unserialize to your notification classes.
Framework mismatch traps.
Notification::route('mail', ...) — Symfony uses Recipient / NotifierInterface.ShouldQueue interface — queueing is decided by transport routing, not the class.php artisan queue:work → php bin/console messenger:consume. Different flags (--limit, --time-limit, --memory-limit), same idea.messenger:consume with --time-limit=3600 and a supervisor that restarts.Debugging. Run the worker with -vv, and use messenger:failed:show to inspect failed envelopes. If notifications silently vanish, check whether the notifier's channel is even configured — Symfony silently skips channels with no transport configured, which has burned many developers.
Extension points to look for. Good bundles expose:
async vs. high_priority).WorkerMessageFailedEvent listener) for alerting.MessageHandlerInterface you can decorate.If none of those exist, treat this as a black box and wrap it — call it from one dedicated service class so you can swap it out when it inevitably goes unmaintained.
Verdict for day-to-day use: the concept is sound and the pattern (queue + notifier + worker) is exactly right for production SaaS apps. But with 0 stars and 0 dependents, adopt only on a project where you're comfortable owning the source — fork it, pin it, or reimplement in half a day. Don't put it in anything you can't easily rip out.
How can I help you explore Laravel packages today?