Maison >développement back-end >tutoriel php >Implémentation d'architectures basées sur les événements en PHP : une plongée approfondie dans le sourcing d'événements et le CQRS

Implémentation d'architectures basées sur les événements en PHP : une plongée approfondie dans le sourcing d'événements et le CQRS

Patricia Arquette
Patricia Arquetteoriginal
2024-09-22 06:20:48397parcourir

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

L'architecture pilotée par les événements (EDA) se concentre sur le découplage des systèmes et sur leur capacité à les rendre plus flexibles, évolutifs et maintenables en répondant aux événements. En PHP, deux modèles importants qui sont souvent utilisés dans EDA sont Event Sourcing et Command Query Responsibility Segregation (CQRS). Voici un guide étape par étape pour les implémenter à l’aide de PHP, ainsi qu’un exemple pratique.

Aperçu des concepts

1. Sourcing d'événements :

  • Au lieu de conserver uniquement l'état final de l'application dans la base de données, chaque modification (événement) de l'état de l'application est stockée.
  • Exemple : Si vous disposez d'un système de commande, au lieu de stocker uniquement le dernier statut de la commande, vous stockez chaque action sur la commande comme "Commande créée", "Article ajouté", "Commande payée", etc.

2. CQRS :

  • CQRS sépare les opérations lecture (requête) et écriture (commande). Les deux modèles peuvent évoluer séparément, le modèle d'écriture se concentrant sur la logique métier et la validation, et le modèle de lecture sur la représentation des données.
  • Exemple : Pour un système complexe comme un site de commerce électronique, la logique de passer une commande (écrire) pourrait être séparée de la récupération des détails de la commande (lire).

Flux architectural

  1. Commande :

    • Une commande est une action qui demande un changement d'état (par exemple, "PlaceOrder", "AddItemToOrder").
    • La commande est gérée par un Gestionnaire de commandes qui exécute la logique métier et émet des événements.
  2. Événement :

    • Une fois qu'une commande est traitée, un événement (par exemple, "OrderPlaced", "ItemAdded") est déclenché, indiquant que quelque chose d'important s'est produit.
    • Les événements sont immuables et déclenchent des actions dans d'autres parties du système, telles que la mise à jour des modèles de lecture ou la notification de systèmes externes.
  3. Lire le modèle :

    • Le modèle de lecture est tenu à jour en réagissant aux événements. Il est optimisé pour les opérations de lecture et peut avoir un schéma différent de celui du modèle d'écriture.

Exemple étape par étape : un système de commande

Étape 1 : Configuration du projet

Créez une structure de répertoires :

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

Installer les dépendances (par exemple, symfony/event-dispatcher) :

composer require symfony/event-dispatcher

Étape 2 : définir les commandes

Les commandes représentent des actions qui changent l'état. Exemple : 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;
    }
}

Étape 3 : Créer des événements

Les événements décrivent ce qui s'est passé dans le système. Exemple : 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;
    }
}

Étape 4 : Gestionnaires de commandes

Les gestionnaires de commandes exécutent la logique métier réelle et déclenchent des événements. Exemple : 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');
    }
}

Étape 5 : Gestionnaires d'événements (projection de données pour lire des modèles)

Un gestionnaire d'événements écoute des événements spécifiques et met à jour le modèle de lecture. Exemple : 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;
    }
}

Étape 6 : L'assembler

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

Sortie :

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

Étape 7 : Magasin d'événements (facultatif)

Pour un sourcing complet d'événements, vous devez également implémenter un magasin d'événements pour conserver les événements dans une base de données.

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

Répartition pièce par pièce

  1. Création de commande : représente l'intention de l'utilisateur de modifier quelque chose.
  2. Gestion des commandes : logique métier qui traite les commandes et déclenche des événements.
  3. Émission d'événements : les événements déclenchés après la gestion réussie des commandes.
  4. Gestion des événements : projette les données d'événement dans des modèles de lecture pour des requêtes optimisées.
  5. Séparation CQRS : le modèle de commande se concentre sur la logique de domaine, tandis que le modèle de requête est optimisé pour des recherches rapides.
  6. Event Store : éventuellement, conservez les événements pour rejouer l'état en cas de besoin.

Conclusion

Cet exemple démontre une application simple de CQRS et de Event Sourcing en PHP. Avec ces modèles, vous pouvez créer des systèmes bien évolutifs et maintenables, tout en offrant une auditabilité puissante et une gestion flexible de la lecture/écriture. L'architecture peut évoluer avec des projections supplémentaires, une gestion des événements plus complexe et des intégrations externes telles que des files d'attente de messagerie ou des notifications tierces.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn