Implementando Proyecciones de Eventos (Event Sourcing) Simplificadas con PHP
Event Sourcing es un patrón de arquitectura donde el estado de una aplicación no se guarda directamente, sino que se deriva de una secuencia de eventos. Aunque robusto, implementar Event Sourcing completo puede ser complejo. Este post explora una aproximación simplificada, enfocándonos en la creación de proyecciones a partir de eventos en PHP, ideal para escenarios donde la complejidad de un sistema completo de Event Sourcing es innecesaria.
Consideremos un escenario donde queremos rastrear el saldo de una cuenta bancaria. En lugar de guardar el saldo actual directamente en la base de datos, guardaremos eventos como "DepositoRealizado" y "RetiroRealizado". Luego, podemos "proyectar" el saldo actual reprocesando todos los eventos.
<?php
interface EventInterface {
public function getType(): string;
public function getData(): array;
}
class DepositoRealizado implements EventInterface {
private float $cantidad;
public function __construct(float $cantidad) {
$this->cantidad = $cantidad;
}
public function getType(): string {
return 'DepositoRealizado';
}
public function getData(): array {
return ['cantidad' => $this->cantidad];
}
}
class RetiroRealizado implements EventInterface {
private float $cantidad;
public function __construct(float $cantidad) {
$this->cantidad = $cantidad;
}
public function getType(): string {
return 'RetiroRealizado';
}
public function getData(): array {
return ['cantidad' => $this->cantidad];
}
}
class AccountProjector {
public static function projectBalance(array $events): float {
$saldo = 0.0;
foreach ($events as $event) {
if ($event instanceof EventInterface) {
switch ($event->getType()) {
case 'DepositoRealizado':
$saldo += $event->getData()['cantidad'];
break;
case 'RetiroRealizado':
$saldo -= $event->getData()['cantidad'];
break;
}
}
}
return $saldo;
}
}
// Ejemplo de uso
$eventos = [
new DepositoRealizado(100.0),
new RetiroRealizado(50.0),
new DepositoRealizado(25.0)
];
$saldoActual = AccountProjector::projectBalance($eventos);
echo "Saldo actual: " . $saldoActual . "<br>"; // Output: Saldo actual: 75
En este código, definimos una interfaz `EventInterface` y dos clases de eventos concretos: `DepositoRealizado` y `RetiroRealizado`. La clase `AccountProjector` contiene un método estático `projectBalance` que toma un array de eventos y calcula el saldo reprocesando la secuencia de eventos. Este enfoque permite auditoría (ver todos los depósitos y retiros), pero también puede resultar ineficiente si la cantidad de eventos crece demasiado. Se puede mitigar esto creando snapshots periódicos del saldo.
<?php
// Ejemplo de snapshot
class AccountSnapshot {
private float $saldo;
private int $lastEventId; // ID del último evento aplicado al snapshot
public function __construct(float $saldo, int $lastEventId) {
$this->saldo = $saldo;
$this->lastEventId = $lastEventId;
}
public function getSaldo(): float {
return $this->saldo;
}
public function getLastEventId(): int {
return $this->lastEventId;
}
}
// Modificar AccountProjector para usar snapshots
class AccountProjectorWithSnapshots {
public static function projectBalance(array $events, ?AccountSnapshot $snapshot = null): float {
$saldo = $snapshot ? $snapshot->getSaldo() : 0.0;
$lastEventId = $snapshot ? $snapshot->getLastEventId() : 0;
// Filtra los eventos a partir del último evento aplicado al snapshot
$eventsToProcess = array_filter($events, function($event) use ($lastEventId) {
return $event->id > $lastEventId; // Asume que cada evento tiene un ID único
});
foreach ($eventsToProcess as $event) {
if ($event instanceof EventInterface) {
switch ($event->getType()) {
case 'DepositoRealizado':
$saldo += $event->getData()['cantidad'];
break;
case 'RetiroRealizado':
$saldo -= $event->getData()['cantidad'];
break;
}
}
}
return $saldo;
}
}
En este snippet, se introduce la clase `AccountSnapshot` para guardar el estado de la cuenta en un momento dado. El `AccountProjectorWithSnapshots` usa el snapshot como punto de partida y solo reprocesa los eventos que ocurrieron después del snapshot. Esto reduce significativamente el tiempo de procesamiento para cuentas con una gran cantidad de eventos.
Esta implementación simplificada de proyecciones de eventos en PHP es un punto de partida valioso para entender los principios de Event Sourcing sin la complejidad de un framework completo. Adaptar estas técnicas a tus necesidades te permitirá construir aplicaciones más auditables y resilientes.
No hay comentarios:
Publicar un comentario