In questi giorni, insieme al mio team, ci stiamo cimentando nella realizzazione di un progetto piuttosto complesso in tempi (quando mai) a dir poco strettissimi. Si tratta di una condizione piuttosto estrema, tant'è che non è stato affatto facile far digerire a tutti l'idea di pensare ad un'architettura ben fatta, di separare i compiti tra i vari layer e di scrivere gli unit test.
Alla fine per fortuna il buon senso ha prevalso, e la nostra applicazione, ora come ora
- ha il suo bel domain model
- è suddivisa in layer, tutti disaccoppiati ed esposti solo tramite interfacce
- fa uso di inversion of control, dependency injection e aspect oriented programming
Quali vantaggi *PRATICI* e quale *RISPARMIO* (in termini di fatica e tempo) ci ha portato l'evitare di buttar giù la testa e mettersi a scrivere codice?
Sul punto 1 non mi soffermo, è oramai piuttosto noto come il modello tabellare mal si adatti a variazioni dell'informazione da trattare, cose che capitano spesso, soprattutto nelle fasi iniziali di un progetto, in cui i requisiti non sono ancora perfettamente definiti.
Passiamo al punto 2. La suddivisione in layer ha consentito di dividere il nostro team in diversi gruppi, che si sono concentrati separatamente nello sviluppo delle rispettive librerie.
Il team del DAL ha lavorato a stretto contatto con il responsabile dello sviluppo su DB Oracle.
Il team del BL, proprio grazie al fatto che il DAL è esposto tramite interfacce, ha potuto sviluppare la libreria di propria competenza senza che il DAL esistesse, facendo uso di Test Driven Development e di mocking.
Idem dicasi per il team che si è dedicato al presentation layer.
Prima conclusione: creare interazioni loosely-coupled consente di parallelizzare il processo di sviluppo. Con librerie fortemente accoppiate, sarebbe stato un dannato incubo.
Punto 3. Lo scenario di deploy sarà sicuramente distribuito: il database non sarà direttamente esposto al web server su cui girerà la nostra applicazione, che dovrà pertanto interagirvi tramite web service. Ottimo. Peccato che attualmente
- Non c'è tempo per realizzarli
- Ancora non è chiaro quale tecnologia utilizzare (WCF? WSE? ASP.NET?)
Ma a noi cosa ci frega? I layer comunicano esclusivamente tramite interfacce, quindi posso usare tutta la inversion of control che voglio: al momento, in cui non ci sono risorse da dedicare allo sviluppo dei web service, espongo direttamente le librerie che connettono i vari strati; domani, quando ci sarà più calma, realizzerò lo strato di trasporto, che esporrò tramite le stesse interfacce ed in maniera del tutto trasparente ai layer utilizzatori: ma se oggi per caricare un utente scrivo
IUserServices userServices = ServicesRepository.Get<IUserServices>();
User currentUser = userServices.LoadById(userId);
domani, quando dentro quello IUserServices ci sarà un proxy che si collegherà ad un web service, il mio codice continuerà ad essere perfettamente valido e funzionante. Senza una buona architettura, avrei dovuto realizzare un qualcosa che in seguito avrei dovuto smontare completamente e ricreare da zero.
Ma ancora... il fatto di disaccoppiare gli strati applicativi mi consente di iniettare logica in maniera assolutamente pervasiva: fino ad oggi, leggere una entity da DB comportava la lettura di tutti gli oggetti associati. So che non è bello, è sicuramente uno spreco di risorse leggere informazioni che probabilmente non saranno mai utilizzate. Ma sono convinto che, dal momento in cui si decide di utilizzare un domain model, bisogna *garantirne* la navigabilità: lo sviluppatore, dopo aver caricato l'oggetto User, deve poterne esplorare le dipendenze quando e come vuole. Ma allora? siamo alle solite... una buona architettura mi ha consentito, con un pizzico di AOP, di implementare un LazyLoad che fa perfettamente al nostro caso e che, meraviglia delle meraviglie, fa il paio con l'inversion of control del paragrafo sopra, funzionando senza incertezze sia con lo strato di servizi "in presa diretta" che con nello scenario distribuito che oggi non c'è ma che domani si dovrà necessariamente realizzare (perché è da specifiche). Prossimamente, riuscirò ad introdurre un sistema di log automatico, quantomeno per chiamate ai servizi e errori, e una cache.
Seconda conclusione: l'uso di IoC, DI e AOP ci consente di impostare il lavoro di progettazione e sviluppo su "ciò che sappiamo che un giorno ci sarà", senza badare a quel poco che c'è oggi. Le funzionalità aggiuntive potranno essere realizzate in maniera del tutto indipendente, man mano che si libereranno risorse, e poi introdotte a costo quasi nullo all'interno di quanto già esistente.
Ecco perché, da un paio d'anni a questa parte, la mia passione per questi argomenti è andata via via crescendo e perché sono assolutamente convinto, a dispetto di ciò che molti possono pensare, che una buona ingegnerizzazione del software consente di risparmiare *DA SUBITO* (e non dalla fantomatica versione 2.0 che probabilmente non vedrà mai la luce) tempo, fatica e risorse umane, producendo un risultato qualitativamente con pochi eguali.
Technorati tags:
Architettura