Come modellare gli eventi di dominio in modo che i denormalizzatori possano riempire i modelli di lettura altamente denormalizzati

2

Ho lavorato con un software basandomi strongmente sull'origine degli eventi e sui pattern CQRS per un po 'di tempo e sto cercando di capire come modellare correttamente gli eventi del dominio in relazione alle esigenze dei modelli letti.

Proviamo a descrivere la domanda che mi sto ponendo in modo più formale. In un evento di sourcing dell'applicazione un evento di dominio è un oggetto che rappresenta qualcosa che è accaduto nel dominio e che è rilevante per l'applicazione. Per definizione un evento di dominio dovrebbe essere autonomo nel senso che dovrebbe contenere tutte le informazioni rilevanti su ciò che è accaduto nel dominio, in modo che sia immediatamente utilizzabile senza chiedere informazioni ad altre fonti.

Solitamente, quando viene utilizzato il sourcing di eventi insieme a CQRS, gli eventi vengono pubblicati utilizzando una sorta di bus eventi (potrebbe essere semplice come un bus di eventi in memoria o complesso come un bus di servizio completo), quindi che tutti i denormalizzatori interessati sono in grado di sottoscriverli e aggiornare di conseguenza una sorta di modello di lettura. In tutti gli esempi che ho visto il codice denormalizzatore legge semplicemente le proprietà contenute nell'evento e gestisce una sorta di CRUD sul modello letto (di solito un database relazionale o documentale): l'unica fonte di verità è l'evento e nessuna altra fonte di verità viene interrogata per ottenere le informazioni necessarie per aggiornare il modello di lettura . Questo ha senso per me, facendo in questo modo tutti i modelli di lettura sono indipendenti e possono essere ricostruiti liberamente perché non dipendono gli uni dagli altri (leggere leggere il modello A mentre si gestisce un evento per aggiornare il modello letto B è considerato un anti -pattern).

A condizione che tutte le precedenti dichiarazioni siano corrette (per favore, fammi sapere se non lo sono), ecco il mio dubbio: come posso progettare correttamente la tassonomia di un evento di dominio (intendo le proprietà) se non lo faccio? so in anticipo quali sono tutti i possibili modelli di lettura del mio sistema (e, in generale, non dovremmo cambiare i requisiti per esempio)?

Per chiarire meglio il punto, immagina un dominio di e-commerce in cui gli utenti sono in grado di acquistare libri. Uno degli eventi di dominio più importanti è il seguente:

public class BookPurchased
{
  public Guid UserId { get; set; }
  public Guid BookId { get; set; }
  public Datetime PurchaseDate { get; set; }
  public Guid PurchaseId { get; set; }
}

Immagina che ad un certo punto nel tempo sorga la necessità di supportare i client mobili. In questo scenario abbiamo bisogno di un modello di lettura altamente denormalizzato per il client, in modo che con una sola chiamata a una API di riposo sia in grado di ottenere tutti i dati necessari per visualizzare una pagina di dettaglio sull'acquisto (idealmente vorremmo una veloce API , che può leggere i dati con una singola query su un singolo modello di lettura , senza la necessità di aggregare i dati recuperati con più query). In questo caso, l'evento progettato in precedenza non è adatto per un denormalizzatore incaricato di riempire il nuovo modello letto, perché ad esempio mancano informazioni come il nome completo dell'utente o il titolo del libro. Una forma migliore per l'evento, in questo nuovo scenario, potrebbe essere la seguente:

public class BookPurchased
{
  public Guid UserId { get; set; }
  public string UserFirstName { get; set; }
  public string UserLastName { get; set; }
  public Guid BookId { get; set; }
  public string BookTitle { get; set; }
  public Datetime PurchaseDate { get; set; }
  public Guid PurchaseId { get; set; }
}

Qual è il modo corretto di andare in questo tipo di scenario?

Dovremmo modellare anticipatamente gli eventi "grassi" contenenti tutte le informazioni possibili (anche se non sono strettamente necessari per i casi di utilizzo noti dell'applicazione), in modo da poter supportare modelli di lettura altamente denormalizzati nel caso in cui ne abbiamo bisogno?

Mettendolo in un altro modo, va bene includere alcune proprietà in una classe di eventi di dominio solo per soddisfare la necessità di un singolo modello di lettura specifico?

O forse dovremmo optare per modelli di lettura piccoli e mirati (in modo che possiamo avere eventi di dominio più sottili e il processo di ricostruzione è più veloce) e utilizzare una sorta di "aggregatore intelligente" (come GraphQL ) quando è necessario creare viste altamente denormalizzate dello stato attuale del sistema?

    
posta Enrico Massone 17.10.2018 - 09:42
fonte

1 risposta

2

Gli eventi del dominio devono contenere i dati richiesti e di proprietà del modello Write / the Aggregate. Se il modello Readmodel potrebbe necessitare di dati aggiuntivi non strettamente richiesti da Aggregate e Aggregate lo possiede , è possibile aggiungere queste informazioni all'evento di dominio se questo renderebbe davvero il tuo Readmodel molto più semplice. Ad esempio, potresti includere il valore precedente / precedente di alcune proprietà.

Nel tuo caso, le informazioni aggiuntive necessarie a Readmodel non sono di proprietà di Aggregate (quindi sembra in ogni modo). Ciò significa che non dovresti includerlo. Dovresti passarlo anche nel comando, fino all'evento di dominio. L'aggregato dovrebbe inoltrarlo includendolo nell'evento di dominio e ciò significa che l'aggregato è costretto a conoscere altri aggregati, magari altri contesti limitati.

È tuttavia possibile sottoscrivere gli eventi di dominio emessi da altri aggregati. Nel tuo caso, puoi iscriverti all'evento UserCreated emesso da User Aggregate e potresti mantenere uno stato privato / locale di nomi utente. Quando arriva l'evento BookPurchased , puoi recuperare UserFirstName e UserLastName dallo stato privato.

Ultimamente, per limitare la proliferazione e la duplicazione degli stati privati in Readmodels, ho iniziato a utilizzare le query. Alle domande viene data una risposta da alcuni codici letti canonici. Possono anche essere spinti, quando la risposta viene aggiornata. Sebbene io possa usare un framework per questo, per maggiori informazioni, puoi dare un'occhiata al framework Axon, sezione Query .

    
risposta data 17.10.2018 - 10:13
fonte

Leggi altre domande sui tag