在 PHP 中實作事件驅動架構:深入研究事件溯源與 CQRS

Patricia Arquette
發布: 2024-09-22 06:20:48
原創
356 人瀏覽過

Implementing Event-Driven Architectures in PHP: A Deep Dive into Event Sourcing and CQRS

事件驅動架構(EDA)專注於解耦系統,並透過回應事件使系統更加靈活、可擴展和可維護。在 PHP 中,EDA 中經常使用的兩個重要模式是事件溯源命令查詢職責分離(CQRS)。這是使用 PHP 實作它們的逐步指南,以及實作範例。

概念概述

1. 事件溯源

  • 不只是將應用程式的最終狀態保存在資料庫中,對應用程式狀態的每個變更(事件)都會被儲存。
  • 範例:如果您有訂單系統,您不只儲存最新的訂單狀態,而是儲存訂單上的每個操作,例如「訂單已建立」、「商品已新增」、「訂單已付款”等

2.CQRS

  • CQRS 將讀取(查詢)和寫入(指令)操作分開。這兩個模型可能會單獨發展,寫入模型專注於業務邏輯和驗證,讀取模型專注於資料表示。
  • 範例:對於像電子商務網站這樣的複雜系統,下訂單(寫入)的邏輯可以與獲取訂單詳細資訊(讀取)分開。

架構流程

  1. 指令

    • 指令是請求狀態變更的動作(例如,「PlaceOrder」、「AddItemToOrder」)。
    • 指令由執行業務邏輯並發出事件的 指令處理程序 處理。
  2. 活動

    • 處理指令後,會引發一個事件(例如「OrderPlaced」、「ItemAdded」),表示發生了重要的事情。
    • 事件是不可變的,它們會觸發系統其他部分的操作,例如更新讀取模型或通知外部系統。
  3. 讀取模型:

    • 讀取模型透過對事件做出反應來保持最新。它針對讀取操作進行了最佳化,並且可能具有與寫入模型不同的架構。

逐步範例:訂單系統

第 1 步:設定項目

建立目錄結構:

event-driven-php/
    ├── src/
    │   ├── Commands/
    │   ├── Events/
    │   ├── Handlers/
    │   ├── Models/
    │   └── ReadModels/
    ├── tests/
    └── vendor/
登入後複製

安裝依賴項(例如 symfony/event-dispatcher):

composer require symfony/event-dispatcher
登入後複製

第 2 步:定義指令

指令代表改變狀態的動作。範例: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;
    }
}
登入後複製

第 3 步:建立事件

事件描述了系統中發生的事情。範例: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;
    }
}
登入後複製

第 4 步:命令處理程序

命令處理程序執行實際的業務邏輯並引發事件。範例: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');
    }
}
登入後複製

第 5 步:事件處理程序(投影資料以讀取模型)

事件處理程序偵聽特定事件並更新讀取模型。範例: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;
    }
}
登入後複製

第 6 步:將其放在一起

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);
登入後複製

輸出:

Array
(
    [orderId] => 123
    [customerId] => cust_001
    [status] => placed
)
登入後複製

步驟7:事件儲存(可選)

對於完整的事件來源,您還需要實作事件儲存將事件儲存到資料庫。

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;
        });
    }
}
登入後複製

逐部分細分

  1. 指令建立:代表使用者更改某些內容的意圖。
  2. 指令處理:處理指令和引發事件的業務邏輯。
  3. 事件發射:成功處理指令後引發的事件。
  4. 事件處理:將事件資料投影到讀取模型中以最佳化查詢。
  5. CQRS分離:命令模型專注於領域邏輯,而查詢模型針對快速查找進行了最佳化。
  6. 事件儲存:可選地,保留事件以在需要時重播狀態。

結論

此範例示範了 CQRSEvent Sourcing 在 PHP 中的簡單應用。透過這些模式,您可以建立可擴展且可維護的系統,同時提供強大的可審核性和靈活的讀取/寫入處理。該架構可以隨著額外的投影、更複雜的事件處理以及訊息佇列或第三方通知等外部整合而成長。

以上是在 PHP 中實作事件驅動架構:深入研究事件溯源與 CQRS的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板