Event-Driven Architecture (EDA) focuses on decoupling systems and making them more flexible, scalable, and maintainable by responding to events. In PHP, two important patterns that are often used in EDA are Event Sourcing and Command Query Responsibility Segregation (CQRS). Here’s a step-by-step guide to implementing them using PHP, along with a hands-on example.
Command:
Event:
Read Model:
Create a directory structure:
event-driven-php/ ├── src/ │ ├── Commands/ │ ├── Events/ │ ├── Handlers/ │ ├── Models/ │ └── ReadModels/ ├── tests/ └── vendor/
Install dependencies (e.g., symfony/event-dispatcher):
composer require symfony/event-dispatcher
Commands represent actions that change the state. Example: PlaceOrderCommand.php.
// src/Commands/PlaceOrderCommand.php class PlaceOrderCommand { public string $orderId; public string $customerId; public function __construct(string $orderId, string $customerId) { $this->orderId = $orderId; $this->customerId = $customerId; } }
Events describe what happened in the system. Example: OrderPlacedEvent.php.
// src/Events/OrderPlacedEvent.php class OrderPlacedEvent { public string $orderId; public string $customerId; public function __construct(string $orderId, string $customerId) { $this->orderId = $orderId; $this->customerId = $customerId; } }
Command handlers perform the actual business logic and raise events. Example: PlaceOrderHandler.php.
// src/Handlers/PlaceOrderHandler.php use Symfony\Component\EventDispatcher\EventDispatcher; class PlaceOrderHandler { private EventDispatcher $eventDispatcher; public function __construct(EventDispatcher $eventDispatcher) { $this->eventDispatcher = $eventDispatcher; } public function handle(PlaceOrderCommand $command) { // Business logic (e.g., check stock, validate order) // Emit the event $event = new OrderPlacedEvent($command->orderId, $command->customerId); $this->eventDispatcher->dispatch($event, 'order.placed'); } }
An event handler listens for specific events and updates the read model. Example: OrderProjection.php.
// src/ReadModels/OrderProjection.php class OrderProjection { private array $orders = []; public function onOrderPlaced(OrderPlacedEvent $event) { // Save or update read model with necessary data $this->orders[$event->orderId] = [ 'orderId' => $event->orderId, 'customerId' => $event->customerId, 'status' => 'placed' ]; } public function getOrder(string $orderId) { return $this->orders[$orderId] ?? null; } }
use Symfony\Component\EventDispatcher\EventDispatcher; // Bootstrapping the system $dispatcher = new EventDispatcher(); $orderProjection = new OrderProjection(); // Register event listeners $dispatcher->addListener('order.placed', [$orderProjection, 'onOrderPlaced']); // Create the command and command handler $command = new PlaceOrderCommand('123', 'cust_001'); $handler = new PlaceOrderHandler($dispatcher); // Handle the command (Place the order) $handler->handle($command); // Query the read model for the order $order = $orderProjection->getOrder('123'); print_r($order);
Output:
Array ( [orderId] => 123 [customerId] => cust_001 [status] => placed )
For full event sourcing, you'd also implement an event store to persist events to a database.
class EventStore { private array $storedEvents = []; public function append(Event $event) { $this->storedEvents[] = $event; } public function getEventsForAggregate(string $aggregateId): array { return array_filter($this->storedEvents, function($event) use ($aggregateId) { return $event->aggregateId === $aggregateId; }); } }
This example demonstrates a simple application of CQRS and Event Sourcing in PHP. With these patterns, you can build systems that scale well and are maintainable, while providing powerful auditability and flexible read/write handling. The architecture can grow with additional projections, more complex event handling, and external integrations like messaging queues or third-party notifications.
The above is the detailed content of Implementing Event-Driven Architectures in PHP: A Deep Dive into Event Sourcing and CQRS. For more information, please follow other related articles on the PHP Chinese website!