Come creare una nuova radice aggregata in CQRS?


10

Come dovremmo creare nuove radici aggregate nell'architettura cqrs? In questo esempio, voglio creare un nuovo AR2 radice aggregato che contenga il riferimento al primo AR1.

Sto creando AR2 usando il metodo AR1 come punto di partenza. Finora vedo alcune opzioni:

  1. All'interno del metodo in AR1 createAr2RootOpt1ho potuto chiamare new AR2()e salvare questo oggetto su db immediatamente usando il servizio di dominio che ha accesso al repository.
  2. Potrei emettere un evento nella prima radice aggregata, ad es. SholdCreateAR2Evente quindi avere una saga senza stato che reagisce su questo e emette un comando CreateAR2Commandche viene quindi gestito e in realtà crea AR2 ed emette AR2CreatedEvent. Nel caso in cui si utilizzi il sourcing degli eventi SholdCreateAR2Eventnon verrebbe conservato nell'archivio eventi, poiché non influisce sullo stato della prima radice aggregata. (O dovremmo ancora salvarlo nell'archivio eventi?)

    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());
        }
    }
    

Qual è il modo più corretto per farlo?

Risposte:


2

Penso che l'opzione no. 2 è la soluzione, con una piccola ma importante modifica: AR1non dovrebbe emettere un evento il cui scopo è quello di creare il AR2, invece dovrebbe emettere un AR1WasCreatedevento. Questo evento dovrebbe essere mantenuto nell'archivio eventi, in quanto è un evento importante che segna la nascita di AR1. Poi, un Sagalistent whould per AR1WasCreatedeventi e generare un comando per creare AR2: CreateAR2Command.

L'opzione n. 1 è molto sbagliata. Non si dovrebbe mai iniettare quel tipo di servizio di dominio in un Aggregate. Aggregatesdovrebbe essere puro, senza effetti collaterali se non la generazione di eventi.

PS Non emetto mai eventi dal costruttore di in Aggregatequanto esiste una distinzione tra la creazione di un'istanza di un oggetto (nel senso del linguaggio di programmazione) e la creazione (la nascita se si desidera) di un Aggregate. Emetto eventi solo da un handlemetodo (durante la gestione di a command).


Cosa intendi con AR1WasCreated? Dovrebbe essere AR2WasCreated? Inoltre, se uso la tua logica, emetto un evento AR2WasCreatedprima che venga effettivamente creato? E salvare questo evento nel registro eventi di AR1 sembra problematico, dal momento che in realtà non ho bisogno di questi dati all'interno di AR1 (non modifica nulla all'interno di AR1).
Bojan Vukasovic,

OK, 3 anni dopo. Va AR1WasCreated-> SAGA (ha la regola se A1 è stato creato, quindi crea A2) -> CreateAR2Command-> AR2WasCreated.
Bojan Vukasovic,

@BojanVukasovic Sono contento che abbia funzionato come ho scritto :)
Constantin Galbenu il

2

Come dovremmo creare nuove radici aggregate nell'architettura cqrs?

I modelli di creazione sono strani .

Udi Dahan ha alcune cose utili da dire sul problema generale: non creare radici aggregate . Il punto fondamentale è che l'aggregato non si limita a emergere dal nulla e che esiste un linguaggio di dominio che descrive il modo in cui appaiono, che dovrebbe essere acquisito nel modello del dominio.

Il punto in cui tende a essere distorto è che l'entità nel modello di dominio che sta elaborando il comando non è l'entità che viene modificata dalla transazione. Non è sbagliato; è solo strano (rispetto ai casi in cui si chiede a un'entità di modificarsi.

Anche il tuo secondo approccio è OK. "Gli eventi che generiamo senza effettivamente salvare nel database" vengono talvolta definiti "eventi di dominio"

L'idea di base è che, all'interno della stessa transazione, il gestore comandi genera l'evento, che viaggia lungo il bus fino a un gestore eventi che consente al secondo aggregato di crearsi. Forse avrai una coesione del codice leggermente migliore.

Nota: nei sistemi di origine evento, di solito non si utilizzano gli eventi in questo modo.

In caso di utilizzo del sourcing degli eventi ShouldCreateAR2Event non verrebbe conservato nell'archivio eventi, poiché non influisce sullo stato della prima radice aggregata.

Nota: i nomi degli eventi sono generalmente al passato - ShouldCrateAR2 ha l'ortografia sbagliata.

Sì, se stai semplicemente lanciando un evento sul bus sincrono per eseguire il codice remoto, non dovresti salvare quell'evento nel registro. È solo un dettaglio di implementazione su questa scala.

O dovremmo ancora salvarlo nell'archivio eventi?

Evitare di modificare due flussi di eventi diversi nella stessa transazione. Se questa creazione rappresenta anche una modifica a AR1, la risposta abituale sarebbe quella di modificare AR1 in questa transazione, con un abbonato asincrono a quegli eventi che sono responsabili di lanciare il comando per creare AR2.

La gestione idempotente dei comandi aiuta molto qui.


grazie per la risposta. Un'altra cosa che non è chiara al 100% - in seguito se devo usare AR2 come argomento per AR1 come dovrei passare questo - poiché CQRS afferma che AR dovrebbe essere usato solo per scrivere e non per interrogare. Ma non ho altra scelta che usare AR1.doSmthn(AR2 param)poiché qualsiasi proiezione in lettura che creo non ha dati completi di cui ho bisogno (solo AR2 ha dati completi).
Bojan Vukasovic,

> "Sì, se stai semplicemente lanciando un evento sul bus sincrono per eseguire il codice remoto, non dovresti salvare quell'evento nel registro." Penso che salvarlo abbia un valore reale in quanto sai che il processo è stato avviato perché qualcosa accada, ora puoi anche tracciare se questo è effettivamente completato. Ma immagino che dipenda dal caso d'uso
Chaosekie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.