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

Broadway Bundle Laravel Package

macnificos/broadway-bundle

Symfony bundle for Broadway event sourcing that wires command and event buses, event stores, read models, and sagas into the container, reducing boilerplate when building CQRS and event-driven PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal setup

composer require macnificos/broadway-bundle broadway/broadway

Because the last release is from 2019, Flex will likely not auto-register the bundle. Add it manually in config/bundles.php:

return [
    // ...
    Macnificos\BroadwayBundle\MacnificosBroadwayBundle::class => ['all' => true],
];

Look here first

The single most useful file is src/DependencyInjection/Configuration.php (or Configuration.php at the package root). It enumerates every config key the bundle exposes — event store driver, serializer, command bus middleware, subscriber wiring. Read that before searching the docs, because docs for a 0-star package are effectively non-existent.

Check the README.md and the bundle's services.xml/services.yaml next to see which service IDs (broadway.command_bus, broadway.event_bus, broadway.event_store, broadway.read_model.*) are registered and what tags are applied.

Minimal config

# config/packages/broadway.yaml
broadway:
    event_store:
        dbal:
            connection: doctrine.dbal.default_connection
            # table usually defaults to "event_store"; verify in Configuration.php

Create the event-store table once (the bundle does not normally ship migrations — copy the schema from broadway/broadway's dbal docs, or the bundle's Resources/).

First use case

Pick one small aggregate — e.g. a User — and run a full vertical slice:

  1. RegisterUser command + RegisterUserHandler.
  2. User aggregate extending Broadway\Domain\AggregateRoot.
  3. UserRegistered event.
  4. A UserProjector that listens to the event and writes to a read-model table.

Then inject CommandBus into a controller and dispatch. If a row lands in event_store and your read model is populated, the wiring is correct.

Implementation Patterns

Command → aggregate → event

final class RegisterUserHandler implements CommandHandler
{
    public function __construct(private Repository $repository) {}

    public function __invoke(RegisterUser $command): void
    {
        $user = User::register($command->id, $command->email);
        $this->repository->save($user);
    }
}

If the bundle auto-tags handlers via CommandHandler interface, this is enough. If not, add broadway.command_handler (verify the exact tag in services.yaml) — this is the first thing to confirm when "commands are silently ignored."

Read models / projectors

final class UserProjector implements EventListener
{
    public function __construct(private Connection $db) {}

    public function handle(MessageInterface $message): void
    {
        if ($message->getPayload() instanceof UserRegistered) {
            $this->db->insert('user_read', [
                'id'    => $message->getPayload()->id,
                'email' => $message->getPayload()->email,
            ]);
        }
    }
}

Register it as broadway.event_listener (or whatever the bundle's tag is). Prefer one listener per read model and switch on payload type, rather than one listener handling many concerns.

Multiple event stores

Use the bundle's named stores: a DBAL store for production, an InMemoryEventStore for tests. Override the service in config/packages/test/broadway.yaml:

services:
    broadway.event_store:
        class: Broadway\EventStore\InMemoryEventStore

Wrap each integration test in a kernel reset so events don't leak between tests.

Wiring Broadway repositories

The bundle may not auto-wire Broadway\EventSourcing\EventSourcingRepository. If it doesn't, register it yourself:

services:
    App\Repository\UserRepository:
        class: Broadway\EventSourcing\EventSourcingRepository
        arguments:
            - '@App\Domain\User'
            - '@broadway.event_store'
            - '@broadway.event_stream_decorator'

Async work

Broadway itself is synchronous — commands dispatch events inline. For async, attach an event listener that pushes serialized messages to a Symfony Messenger bus, and consume on the other side. Keep the aggregate's own state synchronous; treat projections as the async part.

Testing

Unit-test aggregates by replaying events directly (User::register() then assert getUncommittedEvents()), and integration-test the full stack through the CommandBus. Snapshot tests against SerializablePayload-based events catch serialization regressions early.

Gotchas and Tips

  • The package is unmaintained (last release 2019, 0 stars). Expect friction against modern PHP (8.1+) and Doctrine 3.x. Before committing, run the test suite locally and consider a fork, or just wire broadway/broadway directly into services.yaml — the bundle mostly saves you ~50 lines of DI config.
  • Silent no-ops are the #1 bug. If commands are dispatched but nothing happens, it's almost always a missing/wrong service tag or an autoconfigure issue. Add debug:container --tag=<tag> to your debugging checklist.
  • Event-store schema drift. The bundle does not manage migrations. Copy the DBAL schema and add your own Doctrine migration for it; treat it as a first-class table.
  • Serialization versioning. Broadway serializes event payloads to JSON. Renaming a property or class later breaks replay. Use explicit serializers, keep event classes append-only, and never delete fields without a migration strategy for older events.
  • Snapshots. Wire them early if aggregates may exceed ~100–200 events. Retrofitting snapshotting onto an existing stream is painful.
  • Transactions and ordering. EventSourcingRepository::save() writes the stream; your projector runs in the same request unless decoupled. Wrap read+write in a transaction only when the projector is synchronous; never assume it for async.
  • Config key guessing is wasted effort. Read Configuration.php and services.yaml in the bundle once and write down the real service IDs/tags — that's your internal doc, since the external docs are thin.
  • Extension points. If you need custom behavior, override services by ID (broadway.event_store, broadway.command_bus) rather than subclassing the bundle. The bundle's services are thin — swapping an InMemoryEventStore or adding a middleware via a decorator is the intended escape hatch.
  • Doctrine connection vs. standalone. The DBAL store expects the bundle's connection config. If you use multiple entity managers or a non-default connection, set connection: explicitly.
  • Do a compatibility spike before you commit. Install in a scratch Symfony 6/7 app, run through the Getting Started slice, and check bin/console debug:container broadway.*. If that works, the bundle is worth keeping; if not, drop it and wire Broadway manually.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle