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

Event Bus Laravel Package

innmind/event-bus

A lightweight event bus for PHP from the Innmind ecosystem. Publish domain events and register listeners or handlers to react to them, keeping producers and consumers decoupled behind a small, predictable API that fits neatly into larger Innmind applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is small and archived, so the fastest path is to read the source rather than the README. Start with composer.json (PHP constraint and the innmind/* dependency range), then the interface files in src/ — there are usually only two that matter:

  • EventBus — the dispatcher, effectively dispatch(object $event): void
  • Listener — a callable object, effectively __invoke(object $event): void

Everything else is a concrete implementation of the bus plus the class-name matching strategy. Confirm the exact constructor names against src/ before writing code; the shape below follows the package's conventions.

composer require innmind/event-bus
use Innmind\EventBus\EventBus;
use Innmind\EventBus\Listener;

final class SendWelcomeEmail implements Listener
{
    public function __construct(private Mailer $mailer) {}

    public function __invoke(object $event): void
    {
        // guard, because the parameter is typed `object`
        if (!$event instanceof UserRegistered) {
            return;
        }

        $this->mailer->send(new WelcomeEmail($event->userId));
    }
}

Wire the bus once (a container singleton or a small factory), register the listener, and dispatch:

$bus->dispatch(new UserRegistered($userId));

First use case: decouple side effects from a use case. In your RegisterUser handler, persist the user, then dispatch UserRegistered; let listeners own the email, the audit log entry, and the analytics call. That is the whole value proposition — the handler stops growing every time a new side effect appears.

Implementation Patterns

Decorate the interface, don't fork the implementation. Because EventBus is an interface, the idiomatic extension is a wrapper:

final class LoggingBus implements EventBus
{
    public function __construct(
        private EventBus $inner,
        private LoggerInterface $logger,
    ) {}

    public function dispatch(object $event): void
    {
        $this->logger->info('event.dispatched', ['event' => $event::class]);
        $this->inner->dispatch($event);
    }
}

The same wrapper trick gives you a TransactionalBus (buffer events, flush after commit), a RetryingBus, or a SpyBus for tests.

Keep listeners as thin adapters. A listener should translate one event into one call on a domain service. Business logic inside a listener is invisible to the code that dispatched the event and is the most common way this pattern rots.

Events are immutable value objects. Past-tense names (UserRegistered, not RegisterUser), readonly/final classes, and payloads made of scalars, IDs, and value objects — not entities. If you ever move a listener behind a queue, the event has to survive serialization, and entity graphs never do.

Register listeners in one place. A single factory or container binding that builds the bus with its listener collection keeps the wiring discoverable. Registering at the call site ($bus->register(...) inside a controller) makes the dispatch graph untraceable.

Dispatch at the transaction boundary. Persist first, dispatch after the write succeeds. If listeners touch the same database and one throws, you now need to decide whether the outer transaction rolls back — usually you don't want that, which is exactly what the TransactionalBus decorator above solves.

Testing. For handler tests, inject the EventBus interface and swap in a spy that records dispatched events; assert on the recorded events rather than on listener side effects. For the bus itself, innmind/black-box (the author's property-testing library) is the expected harness if you want to write tests in the same style.

Under Laravel/framework integration, bind EventBus in a service provider as a singleton and resolve listeners from the container. Be deliberate about the boundary with Laravel's own Event facade — running both dispatchers over the same domain events doubles your side effects.

Gotchas and Tips

  • Archived means frozen. No bug fixes, no PHP-version bumps, no security patches. Pin an exact version (or a commit hash) in composer.json and be prepared to vendor the source. The upside: the surface is tiny, so forking or inlining it into your app is a realistic afternoon's work if you need to move off it.
  • Dependency drift is the real risk. Innmind libraries move in major versions together; an old innmind/immutable or reflection constraint can block an upgrade elsewhere in the dependency graph. Check composer why-not before committing.
  • Missing-listener behaviour. Decide early whether a dispatch with no matching listener is a silent no-op or an exception, and test it. In codebases where the bus is shared, silent no-ops hide typo'd event classes for months.
  • Listener ordering. If listeners are stored in a map keyed by event class, iteration order is not registration order and may not be stable across refactors. If order matters, compose listeners explicitly into a sequence and register that as a single listener.
  • Re-entrancy. A listener that dispatches another event whose listener dispatches the first event gives you unbounded recursion with no cycle detection. Guard with a dispatch-depth counter in your decorator, or push follow-up events onto a queue drained after the top-level dispatch.
  • Fail-fast by default. In a synchronous bus, the first throwing listener aborts the remaining ones and propagates to the caller. If a listener is genuinely best-effort (analytics, notifications), wrap it in a SwallowErrors decorator so a flaky mailer cannot fail a successful registration.
  • No static type safety on the event. The signature is object, so listeners must guard with instanceof. If the package offers a ClassName namespace, that is the matching strategy used to resolve a listener from the event's class — check whether it matches superclasses/interfaces, and write your listener guards to agree with it.
  • Debugging. The bus is synchronous, so stack traces are direct and the dispatch is easy to follow. Add a decorated logging bus rather than reaching for a debugger; it gives you a durable trace of which listeners fired for which event.
  • Long-lived workers. In queue workers or Octane-style runtimes, a singleton bus holding listeners that hold request-scoped state will leak. Reset or rebuild the bus per job.
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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core