Quando si parla di DDD ogni componente in gioco ha la sua importanza, ma la “visione d’insieme” e il contenuto informativo contenuto in un aggregate ricopre un ruolo fondamentale di tutto l’ecosistema.
Partiamo da ciò che un aggregate non è, in modo da chiarire subito la strada da non intraprendere 🙂
public class Azienda | |
{ | |
public string RagioneSociale { get; set; } | |
public string CodiceFiscale { get; set; } | |
public string PartitaIva { get; set; } | |
public Indirizzo SedeLegale { get; set; } | |
public Indirizzo SedeOperativa { get; set; } | |
} | |
public class Indirizzo | |
{ | |
public string Via { get; set; } | |
public string Cap { get; set; } | |
public Comune Comune { get; set; } | |
} | |
public class Comune | |
{ | |
public string CodiceIstat { get; set; } | |
public string Descrizione { get; set; } | |
} |
view rawAnemicDomainModel.cs hosted with ❤ by GitHub
Con un’implementazione di questo tipo, abbiamo in prima battuta una “piacevolissima” sensazione di navigabilità, che ci permette, partendo potenzialmente da ogni punto, di raggiungere qualsiasi foglia del grafo.
Questo però vìola le due caratteristiche che un aggregate-root deve avere:
- garantire la consistenza dell’aggregato
- evitare l’accesso a nodi figli
…e se ci pensate bene anche alcuni dei principi dell’OOP, per esempio l’incapsulamento. Con un implementazione di questo genere abbiamo potenzialmente scoperto il fianco ad “attacchi” di ogni tipo, nel senso che chiunque abbia accesso ai nostri oggetti ne può manipolare lo stato, partendo da ogni punto.
In base a quanto ci siamo detti nei post precedenti, un domain model senza behaviors è un domain model monco…o meglio non è un domain model 🙂
Prendiamo uno use-case “reale” che ci permetta di capire, quando parlo di behaviors, a cosa mi riferisco e come viene modificata l’implementazione di conseguenza:
- quando un utente (fisico o virtuale) del sistema modifica la sede operativa di un’azienda deve essere verificata la corretta associazione alla filiale della banca di riferimento secondo algoritmi, potenzialmente modificabili dinamicamente. Fino a che la nuova banca non ha confermato l’autorizzazione ad operare, l’azienda deve entrare in un limbo che non permetta operatività sull’azienda.
Con un “domain model anemico” ed in totale assenza di aggregate-root non abbiamo modo di garantire la consistenza dello stato dell’oggetto azienda e la conoscenza dei processi di business che vengono veicolati dalla nostra applicazione sono sparpagliati qua e là in “strati” diversi rispetto a quello che per me è l’owner assoluto di questo genere di competenze: il domain layer.
Rifattorizziamo l’object model in modo da enfatizzare il comportamento e incapsulare lo stato (due piccioni con una fava):
public abstract class AggregateRoot | |
{ | |
public Guid Id { get; private set; } | |
protected AggregateRoot() | |
{ | |
Id = Guid.NewGuid(); | |
} | |
} | |
public class Azienda : AggregateRoot | |
{ | |
private Indirizzo _sedeOperativa; | |
public void CambiaSedeOperativa(string via, string cap, int idComune) | |
{ | |
// validazione parametri | |
// logica di business | |
// … | |
} | |
} |
view rawAzienda.cs hosted with ❤ by GitHub
In questo modo chiunque voglia cambiare la sede operativa di una particolare azienda, deve passare per forza attraverso le nostre API e da quel punto in poi, il cambio di stato interno, l’interazione con altre componenti del sistema, la consistenza del “grafo” stesso è demandata all’aggregate-root in oggetto: l’unica fonte della verità del nostro dominio.
Attenzione, non è tutto oro quello che luccica, o meglio è “oro”, ma non è così facile da raggiungere: “chiudere” lo stato internamente comporta una pesante rivisitazione di diversi ambiti in cui, con il modello anemico, ci eravamo ormai assestati (vedi persistenza, unit test…). Ma con un po’ di pazienza (e tempo) vediamo tutto!
P.S. a tutti quelli che mi stanno mandando mail, interessati dai post precedenti, innanzitutto grazie per i complimenti 🙂 Vi chiedo di spostare/postare le vostre interessanti domande nel luogo più appropriato: il forum di guisa. Non perchè io non voglia rispondervi, anzi…ma perchè così apriamo la conversazione ad altre menti geniali (e che sia chiaro: non è che io mi reputi tale…).