Event Sourcing dettagli nel modello di dominio

1

Ho studiato DDD e ho sperimentato Event Sourcing come meccanismo di archiviazione.

Prima di questo (principalmente applicazioni in stile CRUD con modelli anemici di dati archiviati in DB relazionali), una "best practice" che ho cercato di tenere a mente è di disaccoppiare il mio modello di dati da eventuali problemi di persistenza.

Tuttavia, ho cercato di capire come farlo con Event Sourcing. Sembra che gli oggetti del mio modello di dominio debbano conoscere gli eventi e generarli da soli, e sapere come ricostruire il loro stato da un flusso di eventi.

L'unico modo in cui posso pensare è che i miei repository ricostruiscano gli oggetti di dominio dagli eventi, e quindi eseguono alcuni elaborati rilevamenti delle modifiche per generare eventi risultanti. Sembra che sarebbe inutilmente complicato e soggetto a errori, così come risulterebbe nella logica del dominio che vive o viene duplicata nelle classi del repository.

O il mio pensiero su questo è tutto sbagliato e dovrei considerare gli eventi come parte del modello di dominio? In questo modo, gli oggetti di dominio che generano eventi e ricostruiscono lo stato dagli eventi hanno molto più senso, e quindi storage & il recupero degli eventi sarebbe un problema di persistenza e sarà implementato al di fuori del livello del dominio.

    
posta Entith 09.07.2018 - 21:45
fonte

3 risposte

0

However, I have been struggling to wrap my head around how to do this with Event Sourcing. It seems that my domain model objects need to know about events and generate them themselves

Sì, è vero.

Quindi i tuoi casi d'uso assomigliano a funzioni che accettano come argomenti un command message e un current state , e restituiscono un list of events .

Inoltre, hai bisogno di una funzione che possa prendere una current state e una list of events e creare da essa un nuovo stato.

Puoi quindi prendere queste funzioni e organizzarle in modi diversi a seconda di come vuoi memorizzare il tuo stato quando non in uso.

Ad esempio, se si prevede di memorizzare "lo stato corrente", si compone qualcosa come

currentState = repo.get()
events = useCase(currentState, commandMessage)
nextState = update(currentState, events)
repo.replace(currentState, nextState)

D'altro canto, se si sta utilizzando un archivio eventi, si potrebbe invece vedere

currentHistory = repo.get()
currentState = update(EmptyState, currentHistory)
events = useCase(currentState, commandMessage)
nextHistory = currentHistory.append(events)
repo.replace(currentHistory, nextHistory)

Separare "calcola il prossimo cambiamento" da "applicare il prossimo cambiamento" non è familiare, perché tendiamo a confondere le linee tra queste due responsabilità. Ma se si rifattano gli oggetti del dominio "normale" con attenzione, è possibile distinguere i due dubbi.

The only way around this I can think of is for my repositories to reconstruct domain objects from events, and then do some elaborate change detection to generate resulting events.

Puoi farlo, in un certo senso: è analogo al calcolo della differenza tra due documenti e alla generazione di una patch. Ma capire la semantica della patch è un problema difficile.

I should consider events as part of the domain model?

Assolutamente. Sono messaggi con semantica di dominio.

    
risposta data 10.07.2018 - 02:38
fonte
0

Sono d'accordo con tutto ciò che VoiceOfUnreason ha da dire, ma volevo commentare questa linea:

The only way around this I can think of is for my repositories to reconstruct domain objects from events, and then do some elaborate change detection to generate resulting events.

Questo rilevamento complesso dei cambiamenti non dovrebbe essere necessario perché il tuo aggregato sa già cosa sta succedendo e sa quindi quali eventi devono essere creati. Ad esempio, quando il cognome di un aggregato Utente cambia, il comando chiamerà il metodo UpdatePersonalInfo sull'aggregato Utente e passerà il nome, il cognome e la data di nascita. Il metodo dovrebbe convalidare i dati in arrivo, quindi apportare la modifica all'aggregato. Poiché l'aggregato Utente sa già cosa sta succedendo, dovrebbe già sapere quali eventi devono essere generati, quindi non è necessario un "rilevamento di modifica elaborato".

Ci si potrebbe chiedere cosa succede se l'utente cambia solo il cognome e non il nome o la data di nascita. Non importa. Pubblichi ancora lo stesso UserPersonalInfoUpdated evento con tutte le informazioni personali, anche se solo una parte di esse è stata modificata. Se un ascoltatore è preoccupato su quale dei 3 campi effettivamente modificati, gestisce tale confronto in modo tale che "l'elaborazione del rilevamento delle modifiche" viene eseguita solo se necessario.

Sospetto che tu stia immaginando eventi che contengono un elenco di campi aggregati modificati insieme ai loro nuovi valori. Il problema con questo approccio è che può ridurre la fedeltà degli eventi generati. Un evento chiamato UserUpdated con un elenco di campi modificati è meno intuitivo di un evento UserPersonalInfoUpdated che contiene tutte le informazioni personali sia che siano state modificate o meno (specialmente se l'aggregato Utente contiene molti campi). Costringe gli ascoltatori a sottoscrivere l'evento generico UserUpdated e analizza i campi modificati alla ricerca di modifiche interessanti, piuttosto che iscriversi a eventi specifici di interesse.

Ovviamente questo aumenterà il numero di eventi che il tuo servizio deve ascoltare, ma con lo schema di denominazione degli eventi intelligente e i filtri del nome evento con caratteri jolly, puoi ottenere notifiche generali e dettagliate per tutti i tuoi aggregati.

    
risposta data 10.07.2018 - 03:14
fonte
0

Gli eventi di dominio sono stati identificati come cittadini di prima classe nel livello del dominio fin dai primi anni di storia del DDD, e gli eventi descritti dall'approccio Event Sourcing, mentre potrebbero differire leggermente dalle altre definizioni, non fanno eccezione.

Esattamente dove si scorre sul flusso di eventi per ricostruire l'aggregato varia dall'implementazione all'implementazione. Alcuni mettono quella logica in una classe di base Aggregate e altre nel codice di Application Service / Command Handler. A volte i repository conoscono solo i flussi di eventi, ma a volte gestiscono i tipi di radice aggregata, quindi potresti probabilmente inserire quel loop (o piega ) anche lì.

Il codice di mutazione aggregato basato su ogni Evento specifico, tuttavia, risiede sempre nello stesso Aggregato perché è una logica di dominio. Lo troverai sotto nomi di metodi diversi come apply o evolve . Non è possibile inserirlo in un repository perché la logica del dominio si diffonderà nel livello infrastruttura.

    
risposta data 09.08.2018 - 11:42
fonte

Leggi altre domande sui tag