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

Command Bus Laravel Package

innmind/command-bus

A lightweight command bus for PHP that maps commands to handlers and dispatches them through a middleware pipeline, making it easy to add concerns like transactions, logging, and validation around command execution.

View on GitHub
Deep Wiki
Context7

Getting Started

innmind/command-bus is a small, interface-first PHP library. There is no framework glue, no autodiscovery, and almost no surface area — that is the point. Expect to read the whole src/ directory in one sitting (it is tiny) before writing any code.

Installation and first look:

composer require innmind/command-bus

Note up front that the repository is archived. Composer will still install it from Packagist, but you should treat it as "vendored code with a nice API" rather than a maintained dependency. If your project needs it long-term, fork it, pin the commit SHA, or copy the interface into your own namespace and reimplement the trivial parts.

Where to look first, in order:

  1. src/CommandBus.php (or similarly named interface) — this is the contract you will type-hint against everywhere.
  2. The concrete implementation(s) shipped in src/ — usually a synchronous dispatcher that maps a command class to a handler.
  3. The composer.json require block — it will pull in other innmind/* packages (immutable collections, OS/filesystem abstractions), which tells you which style of code the library expects.

Minimal first use case: a single command, a single handler, and one dispatch call. Define the command as an immutable DTO, define the handler as an invokable class, wire the two together, then call the bus from your controller/console command. Do not try to build a CQRS framework on day one; the library deliberately refuses to be one.

Implementation Patterns

Command as an immutable value object. Innmind code leans on immutability, so commands should be final, readonly where your PHP version allows, and free of behaviour. All the intent lives in the constructor arguments. This makes commands trivially serialisable for logging and queueing.

Handler-per-command, not handler-per-bus. The usual shape is one handler class with an __invoke(MyCommand $command) method (or an interface with a handle() method — check the README, it varies across Innmind packages). Kept this narrow, the bus itself stays dumb and testable.

Wrapping the bus rather than extending it. Because the contract is a single method, the cleanest integration is the decorator pattern: implement CommandBus yourself, hold the inner bus, and add cross-cutting behaviour.

final class TransactionalBus implements CommandBus
{
    public function __construct(
        private CommandBus $inner,
        private Connection $db,
    ) {}

    public function dispatch(object $command): void
    {
        $this->db->transactional(fn() => $this->inner->dispatch($command));
    }
}

This is where the library earns its keep — logging, timing, retries, authorisation, and metrics all become one-class wrappers that compose.

Wiring in a service container. The package does not ship a container binding. In innmind/mantle projects you register the bus as a service and inject it; in Laravel/Symfony you bind the interface to a singleton concrete implementation and register handlers as tagged services. Whichever you choose, keep handler resolution in the container, not in the bus.

Testing. Because the contract is one method, a test double is a few lines:

final class RecordingBus implements CommandBus
{
    public array $dispatched = [];
    public function dispatch(object $command): void { $this->dispatched[] = $command; }
}

This is the strongest day-to-day argument for the package: your application code depends on a one-method interface that you can fake instantly, and swapping in an async/queued implementation is a container change rather than an application change.

Async escape hatch. The library gives you a synchronous core, but the decorator pattern means you can add a QueuedBus that serialises commands and pushes them onto a queue, with a worker that reconstructs them and calls the synchronous bus. Nothing in the package fights you on this — but nothing helps you either, so serialisation strategy is entirely yours.

Gotchas and Tips

  • It is archived. No bug fixes, no PHP 8.x compatibility guarantees, no security patches. Budget for a fork or replacement if this becomes load-bearing. Verify the composer.json PHP constraint against your runtime before committing.
  • Zero dependents on Packagist. There is effectively no community, no Stack Overflow history, and no third-party middleware ecosystem. Every pattern above is something you build yourself.
  • Innmind's functional idioms leak through. If the implementation returns Innmind\Immutable\Maybe or similar, do not fight it — pattern-match on the return value rather than adding ->unwrap() calls. Mixing imperative try/catch with Maybe returns is the fastest way to unreadable code.
  • Handler registration is manual. There is no convention-over-configuration. If you don't register a handler, the bus will throw (or return a failure value) at dispatch time, not at boot time. A small container-compile-time check that every command class has a registered handler saves hours of runtime debugging.
  • Exception semantics matter. Decide early whether handlers throw or return failures, and enforce it in one place (a decorator). Mixed conventions are painful to unwind later.
  • Command identity for logging. Since commands are plain objects, give them a stable toArray() or jsonSerialize() for structured logs; var_export on nested value objects produces noise.
  • Do not put logic in the bus. The moment you add routing, middleware ordering, or retry logic inside a concrete bus implementation, you lose the decorator composability that made the library worth using. Keep the concrete bus a thin map, and wrap.
  • Debugging dispatches. Add a temporary logging decorator that records command class names and elapsed time. Because there is no built-in profiler hook, this is the only visibility you get — but it takes ten lines and is reusable.
  • Extension points are the interface itself. There is no plugin system, no event emitter, no configuration array. The extension mechanism is "implement CommandBus and wrap". Treat any concrete class in the package as an implementation detail you are allowed to reimplement.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
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