¿Cómo crear una nueva raíz agregada en CQRS?

10

¿Cómo debemos crear nuevas raíces agregadas en la arquitectura cqrs? En este ejemplo, quiero crear una nueva raíz agregada AR2 que tenga referencia a la primera AR1.

Estoy creando AR2 usando el método AR1 como punto de partida. Hasta ahora veo pocas opciones:

  1. Dentro método en AR1 createAr2RootOpt1que podría llamar new AR2()y guardar este objeto db imediatelly utilizando el servicio de dominio que tenga acceso al repositorio.
  2. Podría emitir evento en la primera raíz agregada, por ejemplo. SholdCreateAR2Eventy luego tienen una saga sin estado que reacciona ante esto y emite un comando CreateAR2Commandque luego se maneja y realmente crea AR2 y emite AR2CreatedEvent. En caso de utilizar el abastecimiento de eventos SholdCreateAR2Event, no se conservaría en el almacén de eventos, ya que no afecta el estado de la primera raíz agregada. (¿O deberíamos guardar esto en la tienda de eventos?)

    class AR1{
        Integer id;
        DomainService ds;
    
        //OPTION 1
        void createAr2RootOpt1(){
            AR2 ar2 = new AR2();
            ds.saveToRepo(ar2);
        }
    
        //OPTION 2
        void createAr2RootOpt2(){
            publishEvent(new SholdCreateAR2Event());    //we don't need this event. Shoud it still be preserved in event store?
        }
    }
    
    class AR2{
        Integer id;
        Integer ar1Id;
    
        void handle(CreateAR2Command command){
            //init this AR with values and save
            publishEvent(AR2CreatedEvent());    //used for projections afterwards and saved inside AR2 event store
        }
    }
    
    class Saga{
        void handle(SholdCreateAR2Event ev){
            emitCommand(new CreateAR2Command());
        }
    }
    

¿Cuál es la forma más adecuada de hacer esto?

Bojan Vukasovic
fuente

Respuestas:

2

Creo que esa opción no. 2 es la solución, con una modificación pequeña pero importante: AR1no debe emitir un evento cuyo propósito es crear el AR2, sino que debe emitir un AR1WasCreatedevento. Este evento debe persistir en la tienda de eventos, ya que es un evento importante que marca el nacimiento de AR1. A continuación, un Sagalistent whould de AR1WasCreatedevento y generar un comando para crear AR2: CreateAR2Command.

La opción n. ° 1 está muy mal. Nunca debe inyectar ese tipo de servicio de dominio en un Aggregate. Aggregatesdebe ser puro, sin efectos secundarios distintos a la generación de eventos.

PD: Nunca emito eventos del constructor de la, Aggregateya que hay una distinción entre crear una instancia de un objeto (en el sentido del lenguaje de programación) y la creación (el nacimiento si lo desea) de un Aggregate. Emito eventos solo de un handlemétodo (cuando manejo a command).

Constantin Galbenu
fuente
¿Qué quieres decir con AR1WasCreated? Debe ser AR2WasCreated? Además, si uso su lógica, ¿emito un evento AR2WasCreatedantes de que se cree realmente? Y guardar este evento dentro del registro de eventos de AR1 parece problemático, ya que en realidad no necesito estos datos dentro de AR1 (no modifica nada dentro de AR1).
Bojan Vukasovic
Ok, 3 años después. Va AR1WasCreated-> SAGA (tiene una regla si se creó A1 y luego crea A2) -> CreateAR2Command-> AR2WasCreated.
Bojan Vukasovic
@BojanVukasovic Me alegro de que funcionó como escribí :)
Constantin Galbenu
2

¿Cómo debemos crear nuevas raíces agregadas en la arquitectura cqrs?

Los patrones de creación son raros .

Udi Dahan tiene algunas cosas útiles que decir sobre el problema general: no crear raíces agregadas . El punto básico es que el agregado no solo aparece de la nada, y que hay un lenguaje de dominio que describe cómo aparecen, que debe capturarse en su modelo de dominio.

Donde tiende a torcerse es que la entidad en su modelo de dominio que está procesando el comando no es la entidad que está siendo modificada por la transacción. Eso no está mal; es extraño (en comparación con los casos en que le pide a una entidad que se modifique a sí misma).

Su segundo enfoque también está bien. Los "eventos que generamos sin guardar realmente en la base de datos" a veces se denominan "eventos de dominio"

La idea básica es que, dentro de la misma transacción, el controlador de comandos genera el evento, que viaja a lo largo del bus hasta un controlador de eventos que permite que se cree el segundo agregado. Obtendrá una cohesión de código algo mejor, tal vez.

Nota: en los sistemas con fuente de eventos, generalmente no se usan eventos de esta manera.

En el caso de utilizar el abastecimiento de eventos, DoesCreateAR2Event no se conservaría en el almacén de eventos, ya que no afecta el estado de la primera raíz agregada.

Nota: los nombres de los eventos suelen estar en tiempo pasado: ShouldCrateAR2 tiene una ortografía incorrecta.

Sí, si solo está lanzando un evento en el bus síncrono para ejecutar código remoto, entonces no debería guardar ese evento en el libro de registro. Es solo un detalle de implementación a esta escala.

¿O deberíamos guardar esto en la tienda de eventos?

Evite modificar dos secuencias de eventos diferentes en la misma transacción. Si esta creación también representa un cambio en AR1, entonces la respuesta habitual sería modificar AR1 en esta transacción, con un suscriptor asíncrono a esos eventos que es responsable de disparar el comando para crear AR2.

El manejo de comandos idempotentes ayuda mucho aquí.

VoiceOfUnreason
fuente
gracias por responder. Una cosa más que no está 100% clara, más adelante si tengo que usar AR2 como argumento para AR1, ¿cómo debo pasar esto? Ya que CQRS establece que AR solo debe usarse para escribir y no consultar. Pero no tengo otra opción que usar, AR1.doSmthn(AR2 param)ya que cualquier proyección de lectura que creo no tiene datos completos que necesito (solo AR2 tiene datos completos).
Bojan Vukasovic
> "Sí, si solo está lanzando un evento en el bus síncrono para ejecutar código remoto, entonces no debería guardar ese evento en el libro de registro". Creo que guardarlo tiene un valor real porque sabes que el proceso se inició para que algo suceda, ahora también puedes realizar un seguimiento de si esto realmente se completó. Pero supongo que depende del caso de uso
Chaosekie