matiux/broadway-bundle
Symfony bundle that integrates Broadway, the event sourcing and CQRS library for PHP. It wires command and query buses, event stores, read models and event handlers into the container, so you can add CQRS and event sourcing to a Symfony app with minimal boilerplate.
Dublin Bundle is a Symfony bundle that wires the Dublin CQRS/Event Sourcing library into a Symfony application. It is the maintained successor to broadway/broadway-bundle and ships with 1.0.0 as its first stable release. If you are coming from a Laravel background, be aware up front that this is not a Laravel package — there is no service provider, no config/dublin.php, and no facade. It registers as a Symfony Bundle and exposes services through the Symfony DI container. The CQRS/ES concepts (commands, command handlers, events, aggregates, event store, projections) transfer cleanly; the wiring does not.
Where to look first:
composer require matiux/dublin-bundle (and confirm the matiux/dublin constraint resolves to ^3.0).config/bundles.php (Matiux\DublinBundle\DublinBundle::class => ['all' => true]).Resources/config / config/packages/dublin.yaml reference to see which tags (dublin.command_handler, dublin.event_listener, dublin.projector) exist and how the buses are wired.First use case: define a command, an aggregate with handle* methods, a command handler tagged so the bundle registers it on the command bus, and a projector that reads from the event store. Dispatch the command from a controller ($commandBus->dispatch(new CreateThing(...))) and verify the projection updated. That single loop exercises the bundle's default wiring and surfaces almost every configuration issue you'll hit.
Drop-in migration from Broadway Bundle. The release notes explicitly pitch this as a replacement for broadway/broadway-bundle. The mechanical step is swapping the Composer dependency, but you should also grep for Broadway\ namespaces — Dublin is API-compatible in spirit but not byte-for-byte, so imports, tag names, and any custom compiler passes referencing Broadway service IDs need a pass. Do the migration on a branch with the event store read path covered by a test before touching writes.
Tag-based handler discovery. The bundle's main value is auto-registration: tag command handlers, event listeners, and projectors in services.yaml and the bundle wires them onto the correct bus. A typical service definition:
App\Application\Command\CreateUserHandler:
tags: [{ name: dublin.command_handler, bus: command_bus }]
Centralise this in one services.yaml block rather than scattering tags — a misnamed tag means a silently unregistered handler and a command that "dispatches" into the void.
Bus-per-context aliasing. If you need separate command buses (e.g. default, sync, async), define named buses in config and reference them explicitly from handlers and controllers. Avoid relying on the default alias in production code; be explicit so a later config change doesn't route your async work onto the synchronous bus.
Event store as a first-class fixture. In tests, point the event store at an in-memory or SQLite driver rather than the configured production store. Because the bundle wires the store as a service, you can override it per-environment in config/packages/test/dublin.yaml. This is the pattern that keeps event-sourced integration tests fast.
Projectors as the read-model boundary. Treat every projector as the only thing allowed to write to read tables. Application code should query the read model directly, never rehydrate aggregates for reads. This is the discipline that makes the bundle's bus wiring pay off.
Async listeners. Route event subscribers that do slow work (emails, external APIs) to an async transport. The bundle supports standard Symfony messenger integration for the event bus; keep the projector synchronous and offload side effects.
php artisan commands, facades, or a Laravel service provider. If you must use this from a Laravel app, you are effectively embedding a Symfony kernel/container or re-implementing the bundle's wiring by hand — usually the wrong trade. Prefer the standalone matiux/dublin library and wire it yourself, or accept a hybrid setup knowingly.broadway/broadway-bundle config keys, bundle class names, and service tags may differ. Diff your config/packages/*.yaml against the README's example config before trusting it.autoconfigure: true means the handler never reaches the bus, and the failure mode is "nothing happened" rather than an exception. Debug with bin/console debug:container --tag=dublin.command_handler to confirm registration.matiux/dublin:^3.0 alongside the bundle; the notes say compatibility is with Dublin 3.x. Mixing a 2.x Dublin library with a 1.0 bundle will produce confusing service-definition failures.DublinBundle's DependencyInjection/ layer rather than post-processing the container at runtime.ShouldQueue. For async work, lean on Symfony Messenger and configure it explicitly; do not assume Laravel-style dispatch semantics.^1.0 and keep an eye on the changelog; future releases will "track Symfony and PHP evolution more closely," which likely means occasional breaking changes to the wiring contract even within the 1.x series if minimum Symfony/PHP versions are bumped.How can I help you explore Laravel packages today?