Heim  >  Artikel  >  Backend-Entwicklung  >  Implementierung ereignisgesteuerter Architekturen in PHP: Ein tiefer Einblick in Event Sourcing und CQRS

Implementierung ereignisgesteuerter Architekturen in PHP: Ein tiefer Einblick in Event Sourcing und CQRS

Patricia Arquette
Patricia ArquetteOriginal
2024-09-22 06:20:48348Durchsuche

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

Event-Driven Architecture (EDA) konzentriert sich darauf, Systeme zu entkoppeln und sie durch die Reaktion auf Ereignisse flexibler, skalierbarer und wartbarer zu machen. In PHP sind zwei wichtige Muster, die in EDA häufig verwendet werden, Event Sourcing und Command Query Responsibility Segregation (CQRS). Hier finden Sie eine Schritt-für-Schritt-Anleitung zur Implementierung mit PHP sowie ein praktisches Beispiel.

Konzeptübersicht

1. Event-Sourcing:

  • Anstatt nur den Endzustand der Anwendung in der Datenbank zu speichern, wird jede Änderung (Ereignis) am Anwendungsstatus gespeichert.
  • Beispiel: Wenn Sie über ein Bestellsystem verfügen, speichern Sie nicht nur den neuesten Bestellstatus, sondern jede Aktion in der Bestellung, wie „Bestellung erstellt“, „Artikel hinzugefügt“, „Bestellung bezahlt“ usw.

2. CQRS:

  • CQRS trennt Lesen (Abfrage) und Schreiben (Befehl) Vorgänge. Die beiden Modelle können sich getrennt weiterentwickeln, wobei sich das Schreibmodell auf Geschäftslogik und Validierung konzentriert und das Lesemodell auf die Datendarstellung.
  • Beispiel: Bei einem komplexen System wie einer E-Commerce-Website könnte die Logik zum Aufgeben einer Bestellung (Schreiben) vom Abrufen der Bestelldetails (Lesen) getrennt werden.

Architekturfluss

  1. Befehl:

    • Ein Befehl ist eine Aktion, die eine Statusänderung anfordert (z. B. „PlaceOrder“, „AddItemToOrder“).
    • Der Befehl wird von einem Befehlshandler verarbeitet, der Geschäftslogik ausführt und Ereignisse ausgibt.
  2. Ereignis:

    • Nachdem ein Befehl verarbeitet wurde, wird ein Ereignis (z. B. „OrderPlaced“, „ItemAdded“) ausgelöst, das angibt, dass etwas Wichtiges passiert ist.
    • Ereignisse sind unveränderlich und lösen Aktionen in anderen Teilen des Systems aus, beispielsweise die Aktualisierung von Lesemodellen oder die Benachrichtigung externer Systeme.
  3. Modell lesen:

    • Das Lesemodell wird durch die Reaktion auf Ereignisse aktuell gehalten. Es ist für Lesevorgänge optimiert und verfügt möglicherweise über ein anderes Schema als das Schreibmodell.

Schritt-für-Schritt-Beispiel: Ein Bestellsystem

Schritt 1: Projekt einrichten

Erstellen Sie eine Verzeichnisstruktur:

event-driven-php/
    ├── src/
    │   ├── Commands/
    │   ├── Events/
    │   ├── Handlers/
    │   ├── Models/
    │   └── ReadModels/
    ├── tests/
    └── vendor/

Abhängigkeiten installieren (z. B. Symfony/Event-Dispatcher):

composer require symfony/event-dispatcher

Schritt 2: Befehle definieren

Befehle stellen Aktionen dar, die den Zustand ändern. Beispiel: 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;
    }
}

Schritt 3: Ereignisse erstellen

Ereignisse beschreiben, was im System passiert ist. Beispiel: 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;
    }
}

Schritt 4: Befehlshandler

Befehlshandler führen die eigentliche Geschäftslogik aus und lösen Ereignisse aus. Beispiel: 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');
    }
}

Schritt 5: Ereignishandler (Projizieren von Daten zum Lesen von Modellen)

Ein Event-Handler wartet auf bestimmte Ereignisse und aktualisiert das Lesemodell. Beispiel: 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;
    }
}

Schritt 6: Zusammenbau

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);

Ausgabe:

Array
(
    [orderId] => 123
    [customerId] => cust_001
    [status] => placed
)

Schritt 7: Event Store (optional)

Für eine vollständige Ereignisbeschaffung würden Sie auch einen Ereignisspeicher implementieren, um Ereignisse in einer Datenbank beizubehalten.

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;
        });
    }
}

Teilweise Aufschlüsselung

  1. Befehlserstellung: Stellt die Absicht des Benutzers dar, etwas zu ändern.
  2. Befehlsverarbeitung: Geschäftslogik, die Befehle verarbeitet und Ereignisse auslöst.
  3. Ereignisemission: Ereignisse, die ausgelöst werden, nachdem Befehle erfolgreich verarbeitet wurden.
  4. Ereignisbehandlung: Projiziert die Ereignisdaten in Lesemodelle für optimierte Abfragen.
  5. CQRS-Trennung: Das Befehlsmodell konzentriert sich auf die Domänenlogik, während das Abfragemodell für schnelle Suchvorgänge optimiert ist.
  6. Ereignisspeicher: Behalten Sie optional Ereignisse bei, um den Status bei Bedarf wiederzugeben.

Abschluss

Dieses Beispiel zeigt eine einfache Anwendung von CQRS und Event Sourcing in PHP. Mit diesen Mustern können Sie Systeme erstellen, die gut skalierbar und wartbar sind und gleichzeitig eine leistungsstarke Überprüfbarkeit und flexible Lese-/Schreibverarbeitung bieten. Die Architektur kann mit zusätzlichen Projektionen, komplexerer Ereignisbehandlung und externen Integrationen wie Messaging-Warteschlangen oder Benachrichtigungen von Drittanbietern wachsen.

Das obige ist der detaillierte Inhalt vonImplementierung ereignisgesteuerter Architekturen in PHP: Ein tiefer Einblick in Event Sourcing und CQRS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn