miércoles, 25 de junio de 2025

Implementando Event Sourcing Simplificado con PHP y Doctrine

Implementando Event Sourcing Simplificado con PHP y Doctrine

Event Sourcing es un patrón de diseño donde el estado de una aplicación se deriva de una secuencia de eventos. En lugar de persistir el estado actual, se guardan todos los eventos que causaron cambios en ese estado. Esto ofrece ventajas como auditoría completa, capacidad de reconstruir el estado en cualquier momento, y una mejor base para funcionalidades como CQRS (Command Query Responsibility Segregation). Este post presenta una implementación simplificada de Event Sourcing en PHP, utilizando Doctrine para la persistencia.


<?php

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="events")
 */
class Event
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    private $eventType;

    /**
     * @ORM\Column(type="json")
     */
    private $payload;

    /**
     * @ORM\Column(type="datetime_immutable")
     */
    private $occurredAt;

    public function __construct(string $eventType, array $payload)
    {
        $this->eventType = $eventType;
        $this->payload = $payload;
        $this->occurredAt = new \DateTimeImmutable();
    }

    public function getEventType(): string
    {
        return $this->eventType;
    }

    public function getPayload(): array
    {
        return $this->payload;
    }

    public function getOccurredAt(): \DateTimeImmutable
    {
        return $this->occurredAt;
    }
}
    

El código anterior define la entidad Event que se persistirá en la base de datos. Cada evento tiene un eventType (un string que describe el tipo de evento, por ejemplo, 'UserCreated' o 'OrderPlaced'), un payload (un array asociativo que contiene los datos relevantes del evento), y una marca de tiempo occurredAt. Doctrine se encarga de mapear esta clase a una tabla llamada "events".


<?php

use Doctrine\ORM\EntityManagerInterface;

class EventStore
{
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function append(Event $event): void
    {
        $this->entityManager->persist($event);
        $this->entityManager->flush();
    }

    public function replayEvents(): array
    {
        return $this->entityManager
            ->getRepository(Event::class)
            ->findBy([], ['occurredAt' => 'ASC']);
    }
}

    

La clase EventStore gestiona la persistencia y recuperación de los eventos. El método append persiste un nuevo evento en la base de datos, y el método replayEvents recupera todos los eventos en el orden en que ocurrieron. Para reconstruir el estado, se iteraría sobre los eventos recuperados y se aplicarían a un objeto "agregado" (la entidad que representa el estado). Este enfoque simplificado se centra en la persistencia, dejando la lógica de aplicación de los eventos al dominio.

Para usar este sistema, inyectarías el EntityManagerInterface en el EventStore (por ejemplo, usando un contenedor de dependencias). Luego, podrías crear instancias de Event y persistirlas usando $eventStore->append($event). Para reconstruir el estado, usarías $eventStore->replayEvents() y aplicarías cada evento a tu agregado.

No hay comentarios:

Publicar un comentario