Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Async Event Dispatcher Bundle Laravel Package

skydiablo/async-event-dispatcher-bundle

A Symfony bundle for asynchronous event dispatching, letting you offload event listeners to background workers so requests stay fast. Integrates with the event dispatcher and supports configurable async transports.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is a small, unmaintained Symfony 2/3-era package (last release September 2016, 2 stars) that swaps Symfony's event dispatcher for one that pushes events onto a queue and lets a worker process them later. Treat it as reference material rather than a production dependency unless you are pinned to that era's Symfony.

Install and register (legacy Symfony app):

composer require skydiablo/async-event-dispatcher-bundle
// app/AppKernel.php
$bundles = [
    // ...
    new SkyDiablo\AsyncEventDispatcherBundle\AsyncEventDispatcherBundle(),
];

Modern Symfony (4+) uses config/bundles.php instead — but verify PHP/Symfony constraints in composer.json first; the 2016 release will likely conflict with anything past Symfony 3.x.

Where to look first, in order:

  1. DependencyInjection/ — the extension and any compiler pass. This is where the bundle decides whether to decorate/replace the event_dispatcher service, and which config keys it accepts.
  2. Resources/config/services.yml — the transport/queue service definitions. This tells you the actual backend (AMQP, Gearman, Beanstalkd, or an in-process spool).
  3. The dispatcher class itself — look for how it decides which events go async (marker interface, per-event config, or "everything").
  4. Any Command/ directory — this is your worker entrypoint (app/console <bundle>:consume or similar).

First use case: move a slow, non-critical listener off the request cycle. The classic is "send a welcome email after registration." You want the HTTP response to return as soon as the user row is committed, and the email to be rendered/sent by a worker. Anything that touches an external API (mail, webhooks, search indexing, image resizing) is a good first candidate.

Start with a single event and a single listener, confirm the message lands in the queue, then wire the worker under Supervisor before adding more.

Implementation Patterns

Split listeners into sync vs async by what the request needs. The response should not depend on the outcome of an async listener. If the caller must know whether it succeeded, it belongs in a synchronous listener. Domain notifications, projections, and outbound side effects belong in async.

Keep events serializable — carry IDs, not entities.

// Good: plain data
class UserRegistered
{
    public function __construct(public int $userId, public string $email) {}
}

// Fragile: Doctrine entity — proxies and lazy collections do not serialize cleanly
class UserRegistered
{
    public function __construct(public User $user) {}
}

The listener re-fetches the entity from the repository using the ID. This also means the worker sees committed state, not the state at dispatch time.

Worker as a long-lived daemon. Do not run the worker once per message from cron. Run it under Supervisor/systemd, and give it a memory ceiling so it recycles:

[program:async_events]
command=php /var/www/app/console async:worker --env=prod --time-limit=3600 --memory-limit=256
autostart=true
autorestart=true
numprocs=2

Idempotency and retries. Every async event handler must assume it can run twice. Queues redeliver on worker crash or timeout, and there is no exactly-once guarantee. Guard side effects with a dedupe key (event UUID stored in a table, or a unique index on the external call).

Testing pattern. In config_test.yml, point the bundle at a synchronous/in-memory transport if it supports one, or assert against the queue mock. Tests that silently leave messages in a real broker are the fastest way to a flaky CI suite:

# config_test.yml
async_event_dispatcher:
    transport: sync   # or: in_memory — check the extension for the exact key

Integration with existing code. Because the bundle typically replaces the event_dispatcher service via a compiler pass, your existing $dispatcher->dispatch(...) calls keep working — you just change which event classes are routed async. This makes migration incremental: one event at a time.

Modern alternative: on Symfony 4.3+ use symfony/messenger with the event_dispatcher integration instead. You get the same "dispatch to a bus, consume in a worker" model with maintained transports, retry policies, and middleware. If you are starting fresh, this bundle has no reason to exist in your stack.

Gotchas and Tips

It is abandoned and version-locked. Last release 2016. Expect incompatibilities with Symfony 4+, PHP 8, and modern DI compilation. Do not adopt it for new work; if you inherit it, plan a migration path to Messenger.

Failures are invisible to the user. An exception in an async listener fires in a different process, long after the HTTP response. Your web logs show nothing. Log aggressively in every consumer and record the source event ID so you can trace back from a failed job.

Entity IDs are null at dispatch time. If you dispatch an event before EntityManager::flush(), the entity has no ID yet and your serialized payload is useless. Dispatch after flush, or use a post-flush hook / EntityManager event.

Ordering is not guaranteed. With multiple worker processes, two events for the same aggregate can be processed out of order. If order matters for a stream of events, use a single-shard routing key or collapse them into a single "rebuild projection" message.

Container compilation surprises. The compiler pass that rewires the dispatcher can quietly affect other bundles that expect a synchronous dispatcher (e.g. ones that dispatch and read a mutated argument). Run the full test suite after enabling, and check app/console debug:container event_dispatcher --show-private to confirm which class actually won.

Debug mode differences. Some legacy bundles skip the queue in kernel.debug or dev to keep things easy to trace. That means a bug that only appears in prod is easy to miss. Verify behavior explicitly in each environment.

Serialization failures are runtime, not dispatch-time. A closure in a listener argument or a non-serializable object will throw in the worker, not in your request. Add a smoke test that round-trips every event class through the serializer.

Extension points worth knowing: the transport service alias (swap in a test transport), the serializer (replace with JSON + an explicit event map), and the worker command (subclass it to add metrics or signal handling). All three are defined in Resources/config/services.yml, so overriding them is a matter of aliasing before the bundle's pass runs.

Correlation IDs. The single highest-value addition: attach a request/correlation ID to every dispatched event and include it in worker logs. Without it, debugging an async-only failure is guesswork.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource