Dopo la prima lettura del libro di Evans (e vi assicuro che vale la pena di leggerlo almeno due volte), uno dei principi che maggiormente ho apprezzato è senza dubbio quello dell’AGGREGATE ROOT e degli AGGREGATES in generale.
An aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes.
L’importanza di questo pattern è la stessa del BOUNDED CONTEXT perchè porta alla riduzione della complessità, limitando la possibilità di instaurare relazioni tra gli oggetti. Il livello di granularità di frazionamento di questo pattern è minore del BOUNDED CONTEXT e può costituire essenzialmente un ulteriore livello di suddivisione. Partiamo quindi dal problema che vogliamo evitare, che è essenzialmente lo stesso mostrato nel post precedente.
In domini complessi infatti, un singolo BOUNDED CONTEXT può essere molto complesso ed è deleterio permettere ad ogni oggetto di poter dialogare direttamente con qualsiasi altro oggetto nello stesso contesto, perchè si rischia di ricadere in un contesto cosi interconnesso che è poco gestibile. In pratica un AGGREGATE non è altro che un insieme di oggetti che rappresenta un’entità ben definita del dominio e l’AGGREGATE ROOT è l’oggetto che incapsula tutto il gruppo e ne costituisce la barriera di accesso. Non esiste una regola generale per individuare un AGGREGATE, ma in generale, se in un contesto abbiamo oggetti che da soli non hanno molto significato, ma acquistano significato presi assieme, questo potrebbe molto facilmente essere un aggregato.
Se facciamo un esempio nel dominio di un ipotetico gioco di ruolo, potremmo dire che un Guerriero è composto da un insieme di oggetti come ad esempio le caratteristiche (forza, intelligenza, presenza, etc), dalle abilità (uso delle armi, salto, corsa, etc) e tutto questo insieme di oggetti costituisce la rappresentazione in questo BOUNDED CONTEXT dell’entità Guerriero. In questo caso infatti una caratteristica ad esempio non ha alcun senso se non associata al concetto di Guerriero e quindi ne costituisce una parte.
Il primo vantaggio del’AGGREGATE è quello di presentare all’esterno un unico punto di contatto, chiamato AGGREGATE ROOT, in particolare esiste un principio che permette ad un repository di gestire solamente gli aggregate root. L’AGGREGATE ROOT diviene quindi un ulteriore astrazione che favorisce il concetto di incapsulamento. Cosi come una classe protegge il suo stato interno grazie ai metodi e fornisce una interfaccia o contratto che implementa la comunicazione con l’esterno, un’AGGREGATE ROOT incapsula una serie di oggetti che rappresentano l’aggregato..
Grazie a questa astrazione è quindi possibile ridurre il numero di interconnessioni tra oggetti, perché queste ultime sono limitate agli AGGREGATE ROOT.
L’aspetto del salvataggio è particolarmente importante, abbiamo infatti due assunti precisi
1) Solo gli aggregate root possono essere gestiti tramite repository
2) Ogni aggregato ha il suo repository per la gestione della persistenza.
Questo significa che se un particolare aggregato è cosi complesso che ci viene facile salvarlo su un NoSQL, possiamo farlo senza problemi, altri aggregati potrebbero per esempio salvarsi semplicemente serializzando su disco, oppure chiamando un servizio esterno che li persiste on the cloud. Questa affermazione solitamente scatena ogni sorta di obiezioni, primo tra tutti il dire “come faccio I report?”, quando in una interfaccia debbo mostrare dei dati che vengono da aggregati differenti che prestazioni avrò se debbo prendere I dati da sorgenti eterogenee e poi magari aggregare in memoria? Purtroppo come in molte aree del DDD non esiste un’unica risposta, e dobbiamo capire se il vantaggio che abbiamo magari usando un NoSQL non ci fa perdere da altre parti.
Una soluzione possibile è CQRS + DOMAIN EVENTS, ogni cambiamento significativo di un AGGREGATE ROOT genererà infatti un evento di dominio, che dovrà essere gestito da un Handler apposito che terrà aggiornate le classiche viste readonly su cui fare le query. Alcuni db NoSQL come raven hanno dei plugin che permettono automaticamente di tenere sincronizzato un database SQL con alcune proprietà degli oggetti salvati. L’obiettivo è quello di cercare di racchiudere la complessità del problema nel dominio e non nell’infrastruttura che ci sta attorno (leggasi ORM). La morale è, se debbo fare carte false per persistere una gerarchia complessa con un ORM, solo perché “tutto il resto è persistito con un ORM”, probabilmente non abbiamo necessariamente preso la strada giusta e vale la pena di pensare se un AGGREGATE o un intero BOUNDED CONTEXT non possano usare strategie differenti di persistenza.
alk.