Come si applica la regola "Composizione radice" a Domain Driven Design?

2

Sono confuso dall'applicazione della "Composition Root" (CR) per creare aggregati in DDD. Seemann (2012) definisce CR come una "(preferibilmente) posizione unica ... dove i moduli sono composti". Discute per la composizione di grafici di oggetti all'interno di CR, vicino al punto di ingresso dell'applicazione, e mette in guardia contro la tentazione di comporre "classi un po 'alla volta per creare piccoli sottosistemi".

Un aggregato in DDD appare come un tale sottosistema - un (sotto) grafico con entità e oggetti valore. Evans (2004) e altri testi DDD (Vernon 2013, Ghosh 2017) raccomandano la costruzione di aggregati all'interno di fabbriche, situati nel livello del dominio, "vicini" all'aggregato (ad esempio un metodo factory sulla radice aggregata) o come servizi standalone. Questo sembra contraddire l'approccio CR.

La domanda è se e come gli approcci CR e Factory dovrebbero essere combinati in DDD. Ad esempio,

  • Un CR viene inserito all'interno del livello dominio, piuttosto che nella voce dell'app. Forse, le fabbriche sono raggruppate in questa singola posizione
    OR
  • I vantaggi del CR (ad esempio, la "capacità di intercettare i sottosistemi per modificare il loro comportamento"; Seemann) sono irrilevanti per il DDD.
  • CR DI non si applica alla creazione di aggregati ma funziona a un livello di granularità più elevato (ad es. iniezione di servizi). Quindi i metodi CR utilizzano le fabbriche per costruire aggregati, per posizionarli sul grafico dell'oggetto.

Riferimenti
M. Seemann, 2012, "Dependency Injection in .NET"
E. Evans, 2004, "Domain-Driven Design" V. Vernon, 2013, "Implementazione del design basato sul dominio"
D. Ghosh, 2017, "Modellazione del dominio funzionale e reattiva"

    
posta Tupolev._ 28.02.2018 - 17:44
fonte

2 risposte

2

Tipicamente, la root della composizione riguarda la soddisfazione delle dipendenze di vari moduli che costituiscono un processo / applicazione.

Quando Evans descrisse il DDD nel libro blu, stava lavorando nel contesto di un'architettura a tre livelli. Puoi pensare ai livelli come a moduli: un modulo di applicazione, che conosce le interfacce del modulo di dominio ma non le implementazioni e un modulo di dominio che conosce l'interfaccia del modulo di persistenza, ma non la sua implementazione.

Poiché l'accoppiamento tra i moduli è l'interfaccia, piuttosto che l'implementazione, possiamo sostituire un modulo con un altro che implementa la stessa interfaccia, e tutto dovrebbe "funzionare".

Il ruolo della radice della composizione è la selezione delle implementazioni e il cablaggio insieme del grafico delle dipendenze. Quindi la root di composizione conosce abbastanza le implementazioni specifiche per istanziarle, passando l'implementazione appropriata di ciascuna.

Nelle architetture in cui è preferibile l'iniezione del costruttore, questo potrebbe apparire come

PersistenceAPI persistence = new PersistenceModule(...)
DomainAPI domain = new DomainModule(persistence, ...)
Application app = new Application(domain, ...)

app.run()

In generale, i moduli descrivono le loro dipendenze - capacità di cui hanno bisogno ma non implementano per se stessi, e la radice della composizione fa il lavoro di introdurre capacità ai consumatori con produttori di capacità.

Una cosa importante da notare qui; il modello sta rimandando il legame del consumatore a un fornitore di capacità specifico - invece di forzare una scelta quando costruiamo un modulo, ritardiamo tale scelta fino a quando non costruiamo la radice (o, con qualche ulteriore lavoro / assistenza dai framework DI, noi può rinviare la scelta fino al tempo di esecuzione).

Quindi per DDD, la risposta è relativamente semplice: si utilizza l'approccio di composizione radice in quelle parti della soluzione in cui si ricava valore aziendale dal rinvio dell'associazione di un consumatore a un'implementazione specifica.

Esempio: un modo per mantenere lo stato del dominio è scrivere i dati in un RDMBS, possibilmente con l'aiuto di un ORM. Un altro modo sarebbe quello di serializzare quello stato in un documento scritto in un archivio di documenti. Potrebbe trattarsi di una seria serializzazione dell'oggetto, oppure potrebbe essere nella forma di un messaggio / ricordo meno abbinato a una specifica rappresentazione in memoria.

La maggior parte delle app non dovrebbe avere bisogno di preoccuparsi di quale di queste scelte tu faccia; quindi ci può essere un valore aziendale nella creazione di una cucitura che ti permetta di cambiare questa decisione.

Il pattern radice di composizione ti dà un elemento in cui la singola responsabilità consiste nel cucire insieme queste cuciture in un'applicazione funzionante.

    
risposta data 28.02.2018 - 20:11
fonte
0

Questo è il modo in cui affronterò il problema, in base all'eccellente risposta e ai commenti forniti.

Esiste una similitudine tra la Root di composizione e una Radice di aggregazione: la prima compone l'intero grafo dell'applicazione e quest'ultima contiene la sottografia per oggetto locale per un Aggregato (@RobertHarvey). Quindi, tramite una Composition Root, è possibile utilizzare DI per comporre i livelli e i moduli del programma all'interno dei livelli e per scambiare le implementazioni dell'interfaccia come richiesto (@VoiceOfUnreason).

Una factory "near" della radice di aggregazione può essere utilizzata con una granularità più fine, per costruire istanze di un particolare aggregato. Pertanto, la conoscenza viene mantenuta ad esempio locale, su come costruire oggetti valore per popolare l'aggregato e su come convalidarlo con regole specifiche. Inoltre, per un aggregato che rappresenta un concetto di dominio ben formato, potrebbe non essere necessaria alcuna implementazione flessibile dei membri interni. Tuttavia, una DI radice di composizione può essere utilizzata per iniettare le fabbriche di aggregazione nei client (@Euforico).

In sintesi, tenterei un approccio gerarchico, mescolando il DI di livello superiore da un modulo Root di composizione con fabbriche di aggregati di livello inferiore.

    
risposta data 01.03.2018 - 13:03
fonte