ホームページ >バックエンド開発 >PHPチュートリアル >PHP でのイベント駆動型アーキテクチャの実装: イベント ソーシングと CQRS の詳細

PHP でのイベント駆動型アーキテクチャの実装: イベント ソーシングと CQRS の詳細

Patricia Arquette
Patricia Arquetteオリジナル
2024-09-22 06:20:48369ブラウズ

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

イベント駆動型アーキテクチャ (EDA) は、システムを切り離し、イベントに応答することでシステムの柔軟性、拡張性、保守性を高めることに重点を置いています。 PHP では、EDA でよく使用される 2 つの重要なパターンは、イベント ソーシングコマンド クエリ責任分離 (CQRS) です。ここでは、PHP を使用してこれらを実装するためのステップバイステップのガイドと実際の例を示します。

概念の概要

1. イベントソーシング:

  • アプリケーションの最終状態だけをデータベースに保存するのではなく、アプリケーションの状態に対するすべての変更 (イベント)が保存されます。
  • : 注文システムがある場合、最新の注文ステータスのみを保存するのではなく、「注文が作成されました」、「商品が追加されました」、「注文が支払われた」など、注文に関するすべてのアクションを保存します。

2.CQRS:

  • CQRS は、read (クエリ) 操作と write (コマンド) 操作を分離します。 2 つのモデルは別々に進化する可能性があり、書き込みモデルはビジネス ロジックと検証に重点を置き、読み取りモデルはデータ表現に重点を置きます。
  • : 電子商取引サイトのような複雑なシステムの場合、注文を行うロジック (書き込み) と注文の詳細を取得するロジック (読み取り) を分離できます。

アーキテクチャフロー

  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. イベント ストア: 必要に応じて、イベントを永続化して、必要に応じて状態を再生します。

結論

この例は、PHP での CQRSEvent Sourcing の簡単なアプリケーションを示しています。これらのパターンを使用すると、強力な監査機能と柔軟な読み取り/書き込み処理を提供しながら、拡張性に優れ、保守しやすいシステムを構築できます。このアーキテクチャは、追加のプロジェクション、より複雑なイベント処理、メッセージング キューやサードパーティ通知などの外部統合によって拡張できます。

以上がPHP でのイベント駆動型アーキテクチャの実装: イベント ソーシングと CQRS の詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。