← Volver a los artículos
Rendimiento y bases de datos

Event Sourcing en la práctica: no solo versionado de datos, sino una potente herramienta de analítica

Una guía práctica del patrón Event-Sourced Entity en Laravel: auditoría completa, reconstrucción del estado en un punto en el tiempo y por qué los logs de eventos son una mina de oro para la analítica de negocio.

Event Sourcing en la práctica: no solo versionado de datos, sino una potente herramienta de analítica
Artículo destacado ↗

Event Sourcing en la práctica: no solo versionado de datos, sino una potente herramienta de analítica

Hoy quiero hablar de un patrón arquitectónico común en aplicaciones empresariales complejas pero a menudo subestimado: la «Event-Sourced Entity» o, simplemente, un sistema robusto de versionado de datos.

¿Qué es?

En el modelo CRUD clásico, actualizamos un registro directamente en una tabla como products: price = 1000. El historial de cambios se pierde silenciosamente.

El modelo orientado a eventos funciona de manera diferente:

  1. La entidad principal almacena solo el estado actual (por ejemplo, el último precio, el nivel de stock).
  2. Cada cambio (un evento) se registra en una tabla de log separada como una entrada independiente e inmutable.
  3. El estado actual es el resultado de aplicar secuencialmente todos los eventos históricos.

Un ejemplo sencillo en Laravel

Consideremos una entidad ProductPricing.

1. Migraciones:

// Tabla principal (estado actual) Schema::create('product_pricings', function (Blueprint $table) { $table->id(); $table->foreignId('product_id')->constrained(); $table->decimal('price', 10, 2); $table->integer('stock'); $table->timestamps(); }); // Tabla de eventos (historial) Schema::create('pricing_events', function (Blueprint $table) { $table->id(); $table->foreignId('product_pricing_id')->constrained()->onDelete('cascade'); $table->string('type'); // 'price_changed', 'stock_incremented' $table->json('payload'); // { "old_price": 100, "new_price": 120, "reason": "supplier_cost" } $table->timestamp('performed_at'); $table->foreignId('user_id')->nullable(); // Quién lo inició });

2. Modelo y evento:

// Event Data Transfer Object class PriceChangedEvent { public function __construct( public float $oldPrice, public float $newPrice, public string $changedBy, public ?string $reason = null ) {} } // Modelo ProductPricing class ProductPricing extends Model { public function events() { return $this->hasMany(PricingEvent::class); } public function changePrice(float $newPrice, User $user, string $reason): void { // 1. Registrar el evento $this->events()->create([ 'type' => 'price_changed', 'payload' => new PriceChangedEvent( oldPrice: $this->price, newPrice: $newPrice, changedBy: $user->email, reason: $reason ), 'performed_at' => now(), 'user_id' => $user->id, ]); // 2. Actualizar el estado actual $this->update(['price' => $newPrice]); } }

Ventajas de este enfoque:

✅ Auditoría completa y trazabilidad. Sabemos no solo qué cambió, sino cuándo, por quién y por qué. Invaluable para el cumplimiento normativo y la resolución de disputas.

✅ Reconstrucción del estado en un punto en el tiempo. Podemos «reproducir» el modelo hasta cualquier momento pasado recalculando los eventos hasta ese punto.

✅ Lógica de negocio desacoplada. Los eventos son hechos puros. Pueden procesarse de forma asíncrona, enviarse a colas de mensajes (RabbitMQ, Kafka).

✅ Base para CQRS. El log de eventos es una fuente única de verdad perfecta para construir varios modelos de lectura (por ejemplo, especializados para reportes).

Desventajas y complejidades:

❌ Mayor complejidad. La arquitectura se vuelve más compleja que el «CRUD simple». Debe gestionarse la consistencia entre la tabla principal y los eventos.

❌ Sobrecarga de rendimiento. En lugar de una actualización, realizamos al menos dos escrituras. Excesivo para sistemas de alto rendimiento donde el historial no es necesario.

❌ Consultas al historial. El análisis de datos requiere trabajar con grandes flujos de eventos, lo que puede ser no trivial.

¿Dónde es especialmente útil? El caso de uso clave: la analítica.

Este enfoque es una mina de oro para analistas de datos y business intelligence. ¿Por qué?

  1. Análisis de tendencias y dinámicas: Construye fácilmente gráficos de cambios de precios de productos en el tiempo, analiza la estacionalidad y correlaciona con campañas de marketing.
  2. Cálculo de métricas: Precio promedio en un período, tasa de rotación de inventario, frecuencia de ajustes.
  3. Análisis de causa raíz: Agrupar eventos por razón o user_id revela qué impulsa con más frecuencia los cambios: fluctuaciones de divisas, acciones de competidores, demanda estacional.
  4. Modelado «What-If»: Sobre la base de eventos históricos, puedes construir simulaciones. «¿Qué pasa con el beneficio si aumentamos el precio un X% durante el período Y?»

Ejemplo de consulta SQL para analítica:

-- Obtener el historial de precios de un producto específico durante el último mes SELECT DATE(performed_at) as change_date, JSON_UNQUOTE(JSON_EXTRACT(payload, '$.new_price')) as price, JSON_UNQUOTE(JSON_EXTRACT(payload, '$.reason')) as change_reason FROM pricing_events WHERE product_pricing_id = 123 AND type = 'price_changed' AND performed_at >= NOW() - INTERVAL 30 DAY ORDER BY performed_at;

Conclusión

El patrón «Event-Sourced Entity» es más que simplemente «guardar el valor anterior». Es un cambio de paradigma: pasar de pensar en «estados» a pensar en «procesos» e «historial».

Úsalo cuando:

  • Se requieren auditorías legislativas (farmacéutica, finanzas, B2G).
  • El proceso de negocio es una secuencia de eventos significativos (pedido: creado → pagado → cumplido → enviado).
  • Necesitas críticamente analítica profunda sobre métricas clave cambiantes (precios, inventario, tasas, calificaciones).

Es una inversión en arquitectura que se amortiza con transparencia, flexibilidad y profundidad de datos para la toma de decisiones.

Tecnologías y temas

Etiquetas del artículo

No hay artículos que coincidan con estos filtros.

¿Tienes un proyecto o una idea para discutir?

Hablemos ↗