
Recientemente, en el podcast Zinc Prod , mis amigos y yo discutimos el patrón CQRS / ES y algunas características de su implementación en Elixir. Porque Utilizo Laravel en mi trabajo, fue un pecado no profundizar en Internet y no encontrar cómo puede saborear este enfoque en el ecosistema de este marco.
Invito a todos los que están debajo del corte, traté de describir el tema de la manera más abstracta posible.
Algunas definiciones
CQRS (segregación de responsabilidad de consulta de comando): asignación de operaciones de lectura y escritura en entidades separadas. Por ejemplo, escribimos al maestro, leemos desde la réplica. CQRS. Hechos y conceptos erróneos : ayuda a obtener un conocimiento profundo de Zen CQRS.
ES (Event Sourcing): almacenamiento de todos los cambios de estado de una entidad o conjunto de entidades.
CQRS / ES es un enfoque arquitectónico en el que guardamos todos los eventos del cambio de estado de una entidad en la tabla de eventos y agregamos un agregado y un proyector a esto.
Agregado : almacena en la memoria las propiedades necesarias para tomar decisiones de lógica de negocios (para acelerar la escritura), toma decisiones (lógica de negocios) y publica eventos.
Proyector : escucha eventos y escribe en tablas o bases de datos separadas (para una lectura más rápida).

En batalla
Proyector de eventos Laravel - biblioteca CQRS / ES para Laravel
Larabank es un repositorio con un enfoque CQRS / ES. Lo llevaremos a prueba.
La configuración de la biblioteca le dirá dónde buscar y qué es. Nos fijamos en el archivo event -jector.php . De los necesarios para describir el trabajo:
projectors
- projectors
registro;reactors
- registre reactores. Reactor: en esta biblioteca se agregan efectos secundarios al procesamiento de eventos, por ejemplo, en este repositorio, si intenta exceder el límite de retiro tres veces, se escribe el evento MoreMoneyNeeded y se envía un mensaje al usuario sobre sus dificultades financieras;replay_chunk_size
: tamaño del fragmento de reproducción. Una de las características de ES es la capacidad de restaurar el historial de eventos. Proyector de eventos Laravel preparado para una pérdida de memoria durante una operación de este tipo utilizando esta configuración.
Presta atención a la migración. Además de las tablas estándar de Laravel, tenemos
stored_events
: la tabla principal de ES con varias columnas de datos no estructurados para datos de metaeventos, almacenamos los tipos de eventos en una fila. Columna importante aggregate_uuid
: almacena el uido del agregado para recibir todos los eventos relacionados con él;accounts
: la tabla del proyector de cuentas de usuario es necesaria para el rápido retorno de los datos actuales sobre el estado del saldo;transaction_counts
: una tabla del proyector del número de transacciones del usuario, necesarias para la devolución rápida del número de transacciones completadas.
Y ahora propongo salir a la carretera con una solicitud para crear una nueva cuenta.
Creación de cuenta
El enrutamiento de resource
estándar describe AccountsController . Estamos interesados en el método de la store
public function store(Request $request) { $newUuid = Str::uuid();
AccountAggregateRoot hereda la biblioteca AggregateRoot . Veamos los métodos que llamó el controlador.
El método persist
llama al método storeMany
el modelo especificado en la configuración de stored_event_model
como stored_event_model
en nuestro caso, StoredEvent
public static function storeMany(array $events, string $uuid = null): void { collect($events) ->map(function (ShouldBeStored $domainEvent) use ($uuid) { $storedEvent = static::createForEvent($domainEvent, $uuid); return [$domainEvent, $storedEvent]; }) ->eachSpread(function (ShouldBeStored $event, StoredEvent $storedEvent) {
* Proyector en cola
Los proyectores AccountProjector y TransactionCountProjector implementan el Projector
por lo tanto, responderán a los eventos sincrónicamente con su grabación.
Ok, se ha creado una cuenta. Propongo considerar cómo lo leerá el cliente.
Visualización de la factura
Si el proyector de cuentas implementa la interfaz del Proyector en cola , el usuario no verá nada hasta que el evento se procese por turno.
Finalmente, estudiaremos cómo funciona la reposición y el retiro de dinero de la cuenta.
Recarga y retirada
Nuevamente, mire el AccountController :
Considere AccountAggregateRoot
al reponer la cuenta:
public function addMoney(int $amount) { $this->recordThat(new MoneyAdded($amount)); return $this; }
* AgregadoRoot
al retirar fondos:
public function subtractMoney(int $amount) { if (!$this->hasSufficientFundsToSubtractAmount($amount)) {

Conclusión
Traté de describir el proceso de "incorporación" en CQRS / ES en Laravel lo más libre de agua posible. El concepto es muy interesante, pero no sin características. Antes de implementar, recuerde:
- eventual consistencia;
- es deseable usar DDD en dominios separados; no debe hacer un sistema grande completamente en este patrón;
- Los cambios en el esquema de la tabla de eventos pueden ser muy dolorosos;
- Responsablemente, vale la pena abordar la elección de la granularidad de los eventos, cuanto más concretos sean los eventos, más estarán en la mesa y se necesitarán más recursos para trabajar con ellos.
Estaré encantado de notar errores.