Più di un anno fa mi sono trovato davanti alla sfida di riprogettare un’architettura partendo da un datato “ball of mud“. La prima decisione importante è stata evitare il trend dei microservizi, optando invece per un monolite modulare. Ma cosa mi ha portato a questa scelta? Gli Architecture Drivers. 

Cosa sono gli Architecture Drivers?

Gli Architecture Drivers sono i fattori chiave che guidano le decisioni architetturali in un progetto software. Non sono semplici preferenze o trend tecnologici, ma elementi concreti che influenzano direttamente la struttura e il design del sistema.

Costruire una casa

Immagina di dover ristrutturare una casa: non puoi basare le tue decisioni solo sulle tendenze del momento o su preferenze personali. Devi considerare:

Il budget disponibile

Un budget di 100.000€ per ristrutturare casa ti porta a scelte molto diverse da un budget di 20.000€. Dovrai decidere se rifare completamente il bagno o limitarti a cambiare i sanitari, proprio come nel software dovrai scegliere tra una riscrittura completa o un refactoring mirato.

Le necessità di chi ci vive

Una giovane famiglia ha bisogno di spazi diversi rispetto a una coppia di pensionati. Il numero di bagni, la disposizione della cucina, persino l’altezza dei mobili: ogni scelta deve rispondere a esigenze specifiche, non a preferenze astratte.

I vincoli strutturali dell’edificio

Un muro portante non si può abbattere solo perché rovinerebbe la simmetria della stanza. Le fondamenta dell’edificio, l’impianto elettrico esistente, persino l’orientamento delle finestre: sono vincoli che influenzano ogni decisione di ristrutturazione.

La qualità e durabilità dei materiali

Il pavimento più economico potrebbe sembrare un affare oggi, ma dopo due anni di uso intensivo mostrerà tutti i suoi limiti. Le scelte sulla qualità dei materiali hanno un impatto diretto sulla durabilità e sui costi di manutenzione futuri.

Sviluppo software

Allo stesso modo, nel software, gli Architecture Drivers si dividono in quattro categorie fondamentali che lavorano insieme per guidare le nostre scelte:

Vincoli tecnici

Sono i limiti tecnici con cui devi fare i conti, come le competenze del team, gli strumenti disponibili e l’infrastruttura esistente. È come dover costruire con i materiali e gli strumenti che hai a disposizione.

Vincoli di business

Rappresentano le realtà aziendali: budget, tempistiche, risorse umane disponibili. È come avere un budget limitato per la ristrutturazione e dover decidere cosa privilegiare.

Requisiti funzionali

Sono le funzionalità che il sistema deve offrire, come gestire ordini o elaborare pagamenti. È come specificare quante stanze deve avere una casa e come devono essere collegate tra loro.

Attributi di qualità

Definiscono come il sistema deve performare: velocità, sicurezza, scalabilità. È come specificare che una casa deve essere efficiente energeticamente o resistente ai terremoti.

La mia situazione

Mi trovavo davanti a una scelta: seguire il trend dei microservizi o optare per un approccio più conservativo? Analizziamo i driver che hanno guidato la mia decisione.

Vincoli tecnici

Team giovane con competenze .NET

Avevo sviluppatori talentuosi con ottima conoscenza dell’ecosistema .NET. Come affideresti un’auto sportiva a un pilota esperto di quella categoria specifica, aveva senso sfruttare questa expertise.

Poca esperienza distribuita

Il team, seppur competente, aveva limitata esperienza con architetture distribuite. Introdurre microservizi sarebbe stato come insegnare a guidare una Formula 1 a chi ha appena preso la patente.

Sistema legacy monolitico

Il nostro punto di partenza era un sistema monolitico che, nonostante i suoi problemi, funzionava. Era come una casa vecchia ma abitabile: necessitava di ristrutturazione, non demolizione.

Vincoli di business

Continuità dello sviluppo

La priorità era non bloccare lo sviluppo per una lunga fase di formazione. Partire subito con i microservizi senza la giusta preparazione avrebbe potuto portare a scelte architetturali errate e difficili da correggere in futuro.

Delivery continuo

Il business necessitava di vedere progressi tangibili e costanti. Un percorso di formazione e sperimentazione con i microservizi avrebbe rallentato significativamente il delivery di nuove funzionalità.

Rischio architetturale

La scelta di un’architettura complessa senza la giusta esperienza rappresentava un rischio troppo elevato. Era preferibile partire con un approccio più consolidato che potesse evolvere nel tempo, piuttosto che rischiare di costruire un sistema distribuito difficile da gestire e mantenere.

Requisiti funzionali

Accesso ai dati cross-module

Alcuni moduli hanno la necessità di accedere ai dati di altri moduli senza la complessità di chiamate remote o orchestrazioni complesse. In un monolite modulare, questo tipo di comunicazione rimane semplice mantenendo comunque un buon livello di incapsulamento.

Design modulare evolutivo

L’architettura deve essere strutturata in moduli ben definiti fin dall’inizio. Questo non solo mantiene il codice organizzato, ma apre anche la possibilità di estrarre specifici moduli in microservizi quando necessario, senza dover riarchitettare l’intero sistema.

Separazione delle responsabilità

Ogni modulo deve avere un dominio di responsabilità chiaro e definito, pur mantenendo la possibilità di comunicare efficientemente con gli altri moduli. Questo equilibrio tra isolamento e comunicazione è fondamentale per una buona architettura modulare.

Attributi di qualità

Sviluppo e manutenzione efficiente

Il team deve poter sviluppare e mantenere il codice in modo efficiente, concentrandosi sulla logica di business invece che sulla gestione di problematiche distribuite. Un monolite modulare ben strutturato permette agli sviluppatori di lavorare in modo produttivo utilizzando pattern e pratiche familiari.

Testing semplificato

L’architettura deve essere strutturata in moduli ben definiti fin dall’inizio. Questo non solo mantiene il codice organizzato, ma apre anche la possibilità di estrarre specifici moduli in microservizi quando necessario, senza dover riarchitettare l’intero sistema.

Deployment affidabile

Il deployment di una singola applicazione, seppur modulare, riduce significativamente la complessità operativa rispetto a un sistema distribuito. Questo si traduce in rilasci più sicuri e una gestione più semplice degli aggiornamenti, mantenendo comunque la possibilità di evolvere verso un’architettura distribuita in futuro.

Come bilanciare gli Architecture Drivers

Gli Architecture Drivers non operano in isolamento. Nel mio caso, ho dovuto bilanciare diversi fattori apparentemente contrastanti.

Il peso del contesto

Non tutti i driver hanno lo stesso peso. Nel mio caso, i technical constraints(competenze del team)e i business constraints(necessità di delivery continuo)hanno avuto un peso maggiore rispetto ad altri fattori. Questo non significa che gli altri driver fossero meno importanti, ma che il contesto ha determinato le priorità.

Gestire i trade-off

A volte i driver possono entrare in conflitto tra loro. Per esempio, la necessità di accesso ai dati tra moduli(requisiti funzionali)potrebbe suggerire un monolite, mentre future esigenze di scalabilità potrebbero suggerire microservizi. La chiave sta nel trovare una soluzione che soddisfi le necessità attuali mantenendo la flessibilità per il futuro.

L’Evoluzione nel tempo

La bellezza di un’architettura modulare ben progettata è che permette di evolvere nel tempo. I driver di oggi potrebbero non essere quelli di domani, e la nostra architettura deve essere pronta a questo cambiamento.

Conclusione

Gli Architecture Drivers sono stati fondamentali nel guidare la mia scelta verso un monolite modulare. Non si è trattato di seguire un trend o di scegliere l’approccio più semplice, ma di ascoltare attentamente ciò che il contesto ci stava dicendo.

La prossima volta che ti trovi davanti a una decisione architetturale importante, fermati un momento e ascolta i tuoi driver. Ti guideranno verso la soluzione più appropriata per il tuo contesto specifico.