Quando pensiamo all’Architettura di una soluzione Enterprise, di getto immaginiamo il pattern Layer Based, che, a prima vista, ci consente di applicare in modo efficiente il principio di Separation of Concerns, ovvero dell’indipendenza dei moduli e del loro raggruppamento per affinità funzionale. Il tutto consentendoci di approcciare in modo più efficace ad un problema complesso.
Tipica Architettura a Layer
In realtà, questa architettura crea una dipendenza diretta (transitiva) di tutto il sistema dal Repository (Data Layer), trasformando il sistema in una soluzione data-centric. Dalla figura di esempio si vede, inoltre, la dipendenza diretta dai cosiddetti support layer(infrastructure, utility, ecc…), rappresentati in assetto verticale ed utilizzati da tutti i layer orizzontali.
Nonostante questo pattern architetturale sia largamente utilizzato ed apprezzato, possiamo individuare alcuni punti di debolezza non proprio “banali”:
- Forte dipendenza dall’infrastruttura dati;
- Dipendenza diretta dai framework di supporto;
- Al crescere della complessità diventa difficile mantenere la netta separazione dei moduli, individuandone il loro “posizionamento”: spesso si inserisce codice di business all’interno della UI.
Proviamo ora a rappresentare diversamente un’architettura layer-based, in modo da evidenziare esplicitamente quanto appena evidenziati:
Tipica Architettura a Layer (rappresentazione circolare)
In questa nuova conformazione è subito visibile la centralità dei dati e la presenza “invasiva” dei framework di supporto/sviluppo. Ora, la domanda è: perché deve essere così? Perché ci si deve legare a tali strumenti piuttosto che esserne idealmente indipendenti?
Tali questioni non sono, ovviamente, nuove, ed in letteratura esistono quelle che possiamo considerare “evoluzioni” del modello architetturale layer based. In particolare Robert Martin (aka Uncle Bob) e Jeffrey Palermo hanno suggerito due soluzioni, molto simili tra loro: Clean Architecture e Onion Architecture.
Clean Architecture
Onion Architecture
Senza voler entrare nelle specifiche differenze (sostanzialmente poche), è interessante evidenziare come tali architetture siano fortemente basate sul Dependency Inversion Principle (DIP, SOLID):
- High-level modules should not depend on low-level modules. Both should depend on abstractions;
- Abstractions should not depend on details. Details should depend on abstractions;
I layer sono concentrici e la dipendenza è dagli anelli più esterni a quelli più interni, centrando il focus sul Dominio Applicativo e sui relativi Servizi annessi, elementi tanto cari ai fautori di DDD. Il legame con il Repository dei Dati o con i Servizi di supporto avviene tramite Dependency Injection, poiché i livelli CORE definiscono solo le Interfacce con cui interagire. Salterà sicuramente all’occhio dei lettori più attenti che UI, Infrastructure, DB e anche TEST sono posizionati sul livello più esterno: si tratta di un elemento di grande rilievo che consente di avere un nucleo applicativo indipendente da quelle che possiamo definire “facility”. Nonostante ciò, però, la divisione del livello esterno in “comparti”, ne garantisce la consistenza ed evita, ad esempio, che un modulo appartenete alla UI possa dialogare direttamente con uno appartenente ai Dati.
A differenza dell’architettura layer-based, ogni cerchio può accedere a tutti i livelli interni, creando quella che viene definita una relazione di dipendenza verso l’interno (verso il Dominio), del tutto voluta, ma non verso elementi di supporto che posso, e sicuramente lo faranno, variare nel tempo ed essere agevolmente sostituiti.
Onion Dependencies
Nel livello Core troviamo una serie di funzionalità di Domino ben delineate, così come riportato nella figura seguente:
Onion "Core"
Se ora proviamo a fare un’operazione simmetrica a quella precedente (passiamo dalla rappresentazione concentrica a quella lineare), una Onion Architecture potrebbe apparire come segue:
Onion Architecture "flat" view
Andiamo ora a fare il punto di quanto si racchiude nel pattern Onion:
- L’Applicazione è creata intorno ad un modello di Dominio indipendente;
- I layer interni definiscono le interfacce, i layer esterni le implementano;
- La direzione della dipendenza è verso il centro;
- Il core del sistema può essere compilato ed eseguito (con opportuni fake se necessario) in modo indipendente dall’infrastruttura.
Come detto, tutto ciò porta ad vere una “vero” decoupling tra i vari layer, favorendo l’aggiornamento/sostituzione di tutti i moduli infrastrutturali e creando una razionalizzazione dei classici progetti “Common/Infrastructure/ecc”.