Installation
composer require averor/cqrs-es-bundle
Add to config/bundles.php:
return [
// ...
Averor\CqrsEsBundle\AverorCqrsEsBundle::class => ['all' => true],
];
Basic Configuration
Edit config/packages/averor_cqrs_es.yml to define:
message_bus (e.g., symfony/messenger or php-amqplib)event_store (e.g., spiral/event-sourced or eventSauce/event-sourced)serializer (default: Symfony’s Serializer)First Use Case: Dispatching a Command
use Averor\CqrsEsBundle\MessageBus\CommandBusInterface;
class CreateUserCommandHandler implements CommandHandlerInterface {
public function __invoke(CreateUserCommand $command) {
// Handle command logic
}
}
// In a controller/service:
$commandBus = $this->container->get(CommandBusInterface::class);
$commandBus->dispatch(new CreateUserCommand());
First Use Case: Publishing an Event
use Averor\CqrsEsBundle\EventStore\EventStoreInterface;
$eventStore = $this->container->get(EventStoreInterface::class);
$eventStore->appendTo('user-123', new UserCreatedEvent());
CommandBusInterface for write operations.
$commandBus->dispatch(new UpdateUserCommand($userId, ['name' => 'Alice']));
QueryBusInterface for read operations (if integrated with a query bus like league/tactician).
$queryBus->ask(new GetUserQuery($userId));
AggregateRoot interface.
class UserAggregate implements AggregateRoot {
private $id;
private $name;
public function handle(CreateUserCommand $command) {
$this->apply(new UserCreatedEvent($command->name));
}
public function apply(UserCreatedEvent $event) {
$this->name = $event->name;
}
}
$eventStore->load('user-123', UserAggregate::class); // Rehydrates aggregate
$eventStore->persist($aggregate); // Saves events
# config/packages/averor_cqrs_es.yml
averor_cqrs_es:
command_bus:
middleware: ['averor_cqrs_es.middleware.command.logger']
Create middleware:
class CommandLoggerMiddleware implements CommandMiddlewareInterface {
public function handle(CommandMessage $command, callable $next) {
// Pre-processing
$result = $next($command);
// Post-processing
return $result;
}
}
averor_cqrs_es:
message_bus:
command_bus: messenger
event_bus: messenger
Configure Messenger transports in config/packages/messenger.yaml.CommandBusInterface, EventStoreInterface, and QueryBusInterface.
public function __construct(
private CommandBusInterface $commandBus,
private EventStoreInterface $eventStore
) {}
$mockBus = $this->createMock(CommandBusInterface::class);
$this->container->set(CommandBusInterface::class, $mockBus);
EventStore::load() with snapshots for performance.
$eventStore->load('user-123', UserAggregate::class, 100); // Load from snapshot at version 100
dispatchMany() or appendMany().
$commandBus->dispatchMany([new Command1(), new Command2()]);
EventStore::appendTo() with explicit event filtering or middleware to validate event sources.Serializable or configure the serializer in averor_cqrs_es.yml:
averor_cqrs_es:
serializer:
format: 'json'
context: { groups: ['serializable'] }
Transaction component):
$connection->beginTransaction();
try {
$eventStore->appendTo($aggregateId, $event);
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
averor_cqrs_es:
command_bus:
middleware:
- averor_cqrs_es.middleware.command.validation
- averor_cqrs_es.middleware.command.logger
averor_cqrs_es:
debug: true
bin/console debug:container averor_cqrs_es.logger
$events = $eventStore->getEvents('user-123');
$aggregate = new UserAggregate();
foreach ($events as $event) {
$aggregate->apply($event);
}
class ValidateCommandMiddleware implements CommandMiddlewareInterface {
public function handle(CommandMessage $command, callable $next) {
if (!$command->isValid()) {
throw new \InvalidArgumentException('Invalid command');
}
return $next($command);
}
}
EventStoreInterface for alternative backends (e.g., MongoDB, PostgreSQL):
class MongoEventStore implements EventStoreInterface {
public function appendTo(string $aggregateId, EventInterface $event) {
// Custom logic
}
}
averor_cqrs_es:
event_store: averor_cqrs_es.event_store.mongo
MessageBusInterface for non-Symfony/Messenger buses (e.g., RabbitMQ):
class RabbitMqBus implements MessageBusInterface {
public function dispatch(MessageInterface $message) {
// RabbitMQ logic
}
}
$commandBus->dispatch(new CreateUserCommand(), [
new PublishDomainEventMiddleware(new UserCreatedEvent())
]);
$eventStore->listen(UserCreatedEvent::class, function (UserCreatedEvent $event) {
// Update read model
});
How can I help you explore Laravel packages today?