broadway/broadway-bundle
Symfony bundle that integrates Broadway CQRS/ES into your app. Symfony Flex installs and configures it automatically, with in-memory event store and read models by default and options for persistent implementations. Documentation available at broadway.github.io.
Installation:
composer require symfony/flex
composer config extra.symfony.allow-contrib true
composer require broadway/broadway-bundle
Symfony Flex auto-configures the bundle with in-memory implementations.
First Use Case: Define a command and handler to test the command bus:
// src/Command/ExampleCommand.php
class ExampleCommand implements Command
{
public function __construct(public string $message) {}
}
// src/CommandHandler/ExampleCommandHandler.php
class ExampleCommandHandler implements CommandHandler
{
public function handle(ExampleCommand $command): void
{
// Business logic here
}
}
Register the handler in config/services.yaml:
App\CommandHandler\ExampleCommandHandler:
tags: [broadway.command_handler]
Dispatch a Command:
Inject broadway.command_handling.command_bus and dispatch:
$commandBus->dispatch(new ExampleCommand("Hello, Broadway!"));
Event-Driven Architecture:
broadway.domain.event_listener) to update read models:
App\Projector\ExampleProjector:
tags: [broadway.domain.event_listener]
class ExampleProjector implements DomainEventListener
{
public function handle(DomainEvent $event): void
{
// Update read models (e.g., Elasticsearch, MongoDB)
}
}
Metadata Enrichment:
App\Metadata\IpMetadataEnricher:
arguments: ["@request_stack"]
tags: [broadway.metadata_enricher]
Sagas for Long-Running Workflows:
config/packages/broadway.yaml:
broadway:
saga:
enabled: true
App\Saga\ReservationSaga:
arguments: ["@broadway.command_handling.command_bus", "@broadway.uuid.generator"]
tags: [broadway.saga, { type: reservation }]
Persistent Storage:
composer require broadway/event-store-dbal
config/packages/broadway.yaml:
broadway:
event_store: broadway.event_store.dbal
broadway.command_handling.command_bus alongside Symfony Messenger for hybrid architectures.broadway.event_store and broadway.read_model in tests for isolation:
$this->container->set('broadway.event_store', $mockEventStore);
In-Memory Stores in Production:
broadway.yaml as shown in Event Store.Event Serialization:
broadway.simple_interface_serializer by default, which may not support all PHP types (e.g., DateTimeImmutable).broadway.yaml:
broadway:
serializer:
payload: App\Serializer\CustomPayloadSerializer
Saga State Persistence:
broadway/broadway-saga and configure:
broadway:
saga:
state_repository: broadway.saga.state.mongodb_repository
Command Bus Dispatch Events:
dispatch_events: false in broadway.yaml. Set to true if you need to publish events after command handling.UUID Generation:
Uuid component, ensure consistency by using the same generator service.Event Store Inspection: Use Symfony’s debug container to inspect stored events:
./bin/console debug:container broadway.event_store
For DBAL stores, query the event_entry table directly.
Projector Debugging: Log domain events to track projector execution:
broadway:
command_handling:
logger: "@monolog.logger.broadway"
Saga Debugging:
Enable saga logging in config/packages/monolog.yaml:
handlers:
broadway_saga:
type: stream
path: "%kernel.logs_dir%/broadway_saga.log"
level: debug
Custom Event Store:
Implement Broadway\EventStore\EventStore and register it in broadway.yaml:
broadway:
event_store: App\EventStore\CustomEventStore
Read Model Projections:
Extend Broadway\ReadModel\Projector to create custom projections:
class CustomProjection extends Projector
{
protected function whenSomethingHappened(SomethingHappened $event): void
{
// Update your read model
}
}
Metadata Enrichers: Chain enrichers for layered metadata (e.g., user + IP + request ID):
class CompositeMetadataEnricher implements MetadataEnricher
{
public function __construct(private iterable $enrichers) {}
public function enrich(Metadata $metadata): Metadata
{
foreach ($this->enrichers as $enricher) {
$metadata = $enricher->enrich($metadata);
}
return $metadata;
}
}
Command Validation: Use Symfony Validator with Broadway commands:
class ExampleCommandHandler implements CommandHandler
{
public function __construct(private ValidatorInterface $validator) {}
public function handle(ExampleCommand $command): void
{
$errors = $this->validator->validate($command);
if (count($errors) > 0) {
throw new InvalidCommandException($errors);
}
// Proceed
}
}
How can I help you explore Laravel packages today?