Salta al contenuto principale

FAQ

Cos'è l'architettura del software e in cosa differisce dal design?

L'architettura del software è l'insieme delle decisioni strutturali significative su un sistema: i suoi elementi principali, le loro relazioni e gli attributi di qualità che tali decisioni producono. Il design è l'attività più locale di decidere come i singoli elementi vengono implementati. Il test pratico per stabilire se una decisione è "architetturale" è l'impatto e il costo del cambiamento: una decisione è architetturale se invertirla in seguito sarebbe costoso o dirompente, come la scelta di un modello di consistenza, della topologia di deployment o dei confini dei moduli. Il design dettagliato delle classi, al contrario, di solito si può cambiare a basso costo all'interno di un singolo modulo.

Quando dovrei scegliere i microservizi invece di un monolite?

Per default scegli un monolite modulare e passa ai microservizi solo quando hai una ragione concreta e presente. I microservizi offrono deployment e scalabilità indipendenti delle parti, autonomia dei team e isolamento dei guasti, ma impongono una tassa pesante: guasti di rete, consistenza eventuale, transazioni distribuite e il carico operativo di gestire molti servizi. Convengono quando i team sono abbastanza numerosi da possedere i servizi in autonomia, quando parti del sistema hanno esigenze di scaling o disponibilità davvero diverse e quando hai la maturità di piattaforma (CI/CD, osservabilità, on-call) per gestirli. Suddividere troppo presto converte chiamate in-process economiche in difficili problemi di sistemi distribuiti.

Come decido dove tracciare i confini tra servizi o moduli?

Traccia i confini attorno ai bounded context - aree del dominio dove un singolo modello e un linguaggio ubiquo restano consistenti - anziché attorno ai livelli tecnici. I buoni confini hanno alta coesione interna e accoppiamento lasco verso l'esterno, e tendono ad allinearsi alle capability di business e ai team che le possiedono (legge di Conway). Un'euristica utile è che un confine è giusto quando la maggior parte dei cambiamenti tocca un solo lato; se una funzionalità tipica richiede costantemente modifiche coordinate tra due servizi, il confine è nel posto sbagliato. Resisti alla tentazione di suddividere per livello tecnico (un "servizio utenti", un "servizio database") perché ciò massimizza l'accoppiamento chiacchierone tra i confini.

Cosa sono gli attributi di qualità e perché guidano l'architettura?

Gli attributi di qualità sono proprietà non funzionali misurabili - prestazioni, disponibilità, scalabilità, sicurezza, modificabilità, osservabilità - che determinano se un sistema è adatto allo scopo oltre alle sue funzionalità. Guidano l'architettura perché, a differenza delle funzionalità, dipendono soprattutto dalla struttura e sono molto difficili da aggiungere a posteriori. Li si rende concreti con scenari: uno stimolo, l'ambiente, la risposta attesa e una misura della risposta, per esempio "sotto carico di picco (ambiente), una richiesta di checkout (stimolo) ritorna entro 300 ms al p99 (misura della risposta)". Obiettivi vaghi come "il sistema deve essere veloce" non si possono progettare né testare.

Qual è la differenza tra architettura Clean, esagonale e Onion?

Tutte e tre sono espressioni della stessa idea di fondo: tenere la logica di business al centro e far puntare le dipendenze a livello di sorgente verso l'interno, così che il dominio non dipenda da framework, UI o database. L'esagonale (Ports and Adapters) inquadra ciò attorno alle porte che l'applicazione espone e agli adattatori che le implementano. L'Onion dispone la stessa idea come livelli concentrici con il modello di dominio al centro. La Clean Architecture la generalizza e la rende popolare con l'esplicita "regola delle dipendenze". In pratica le differenze sono per lo più di vocabolario ed enfasi; scegliere l'una o l'altra conta molto meno che imporre davvero la direzione delle dipendenze verso l'interno.

Come evito che la mia architettura si degradi nel tempo?

La deriva architetturale avviene quando cambiamenti opportunistici erodono silenziosamente i confini previsti finché il design non è più riconoscibile. La difesa più efficace è rendere le regole architetturali eseguibili come fitness function: controlli automatici in CI che fanno fallire la build quando una regola di stratificazione, una direzione di dipendenza o un budget di latenza viene violato (strumenti come ArchUnit lo fanno per la JVM). Affianca a ciò ADR registrati perché decisioni e motivazioni siano esplicite, revisioni architetturali leggere e regolari, e budget di refactoring per ripagare il debito deliberatamente anziché ignorarlo. Un'architettura descritta solo nei diagrammi, e mai verificata, andrà alla deriva.

Mi serve un API gateway e cosa dovrebbe fare?

Un API gateway è utile quando hai più servizi di backend e client esterni, perché ti dà un unico punto per gestire routing, autenticazione, rate limiting, terminazione TLS e aggregazione di richieste, schermando i client dalla tua topologia interna. Tienilo sottile: dovrebbe gestire preoccupazioni trasversali di bordo, non logica di business, altrimenti diventa un collo di bottiglia e un punto di accoppiamento attraverso cui ogni team deve coordinarsi. Se i diversi tipi di client hanno esigenze molto diverse, valuta un Backend for Frontend per client anziché sovraccaricare un unico gateway generico. Per un singolo servizio, un gateway è di solito un costo superfluo.

Come dovrebbero comunicare i servizi: chiamate sincrone o eventi asincroni?

Il request/response sincrono (HTTP, gRPC) è più semplice da ragionare e dà risultati immediati, ma accoppia la disponibilità del chiamante a quella del chiamato e può creare catene di latenza e guasti a cascata. La messaggistica o gli eventi asincroni disaccoppiano i servizi nel tempo, migliorano resilienza e scalabilità e si adattano a flussi che tollerano la consistenza eventuale, al costo di tracing e ordinamento end-to-end più difficili. Una regola pratica comune è usare chiamate sincrone per le query che richiedono una risposta immediata ed eventi asincroni per i cambiamenti di stato che gli altri servizi devono solo conoscere. Molti sistemi mescolano deliberatamente entrambi anziché sceglierne uno globalmente.

Cos'è la consistenza eventuale e quando è accettabile?

La consistenza eventuale significa che, dopo un cambiamento, le repliche o le viste a valle convergeranno nel tempo allo stesso valore, ma potrebbero essere brevemente non aggiornate. È il modello di consistenza che la maggior parte dei sistemi distribuiti e a microservizi accetta in cambio di disponibilità e tolleranza alle partizioni, come inquadrato dal teorema CAP. È accettabile ogniqualvolta il business può tollerare una breve finestra di obsolescenza - un conteggio di "mi piace", un indice di ricerca, un feed di notifiche - cioè la maggior parte delle volte. Non è accettabile per gli invarianti che devono valere istantaneamente, come impedire una doppia spesa; quelli appartengono a un singolo confine di consistenza (un aggregato o una singola transazione di database).

Come gestisco le transazioni che attraversano più servizi?

Non usare il commit distribuito a due fasi tra servizi autonomi; scala male e si blocca quando il coordinatore fallisce. Usa il pattern saga: modella la transazione di business come una sequenza di transazioni locali, ciascuna con un'azione compensativa che la annulla semanticamente se un passo successivo fallisce. Le saga esistono nella forma a orchestrazione, in cui un coordinatore guida esplicitamente i passi, e a coreografia, in cui i servizi reagiscono agli eventi degli altri. Qualunque tu scelga, rendi ogni passo idempotente affinché i retry siano sicuri, e accetta che l'operazione complessiva sia eventualmente consistente anziché atomica.

Quali pattern di resilienza dovrebbe avere ogni sistema distribuito?

Come minimo: timeout su ogni chiamata remota affinché una dipendenza lenta non possa bloccare i tuoi thread a tempo indeterminato; retry con backoff esponenziale e jitter per i guasti transitori, applicati solo a operazioni idempotenti; circuit breaker che falliscono rapidamente quando una dipendenza è chiaramente giù; e bulkhead che isolano i pool di risorse affinché una dipendenza in errore non possa affamare le altre. Aggiungi una degradazione controllata affinché il sistema offra un'esperienza ridotta ma utile quando una dipendenza non critica è indisponibile. L'obiettivo unificante è contenere il guasto e ridurne il raggio d'azione anziché presumere che le dipendenze non falliscano mai.

Quanta architettura dovrei fare a monte rispetto a quella emergente?

Decidi a monte solo le cose costose da cambiare in seguito e vincola tutto il resto - il modello di consistenza, i confini principali, lo stile di deployment e integrazione e gli obiettivi dominanti di attributi di qualità. Rinvia le decisioni economiche da cambiare ("ultimo momento responsabile") e lascia che la struttura dettagliata emerga tramite refactoring man mano che impari il dominio. Questa via di mezzo evita sia il big design up front, che scommette pesantemente su una conoscenza incompleta, sia l'assenza totale di design, che produce un groviglio costoso da districare. L'architettura evolutiva formalizza ciò proteggendo i pochi vincoli rigidi con fitness function e lasciando spazio per evolvere.

Cos'è il modello C4 e perché è popolare?

Il modello C4 descrive un sistema con quattro livelli annidati di diagramma - System Context, Containers, Components e Code - così che ogni diagramma abbia un pubblico e un livello di astrazione chiari. La sua popolarità deriva dall'essere semplice, leggero nella notazione e coerente, ciò che mantiene i diagrammi comprensibili e manutenibili anziché trasformarli in una proliferazione UML che nessuno aggiorna. In pratica, la maggior parte dei team ricava il massimo valore dai livelli Context e Container, scendendo ai Component solo per le parti che lo meritano. Complementa, anziché sostituire, gli ADR, che catturano il "perché" che i diagrammi non possono esprimere.

Come documento un'architettura affinché resti utile?

Prediligi artefatti leggeri e vivi rispetto a un unico documento pesante che è obsoleto il giorno in cui viene approvato. Registra le decisioni significative come ADR committati con il codice, disegna un piccolo numero di diagrammi C4 ai livelli che contano, e scrivi gli scenari di attributi di qualità affinché gli obiettivi siano espliciti e verificabili. Tieni la documentazione vicina al codice e aggiornala come parte del cambiamento che la riguarda, anziché come una cerimonia separata. L'obiettivo è una documentazione che risponda alle domande che i nuovi arrivati e i revisori si pongono davvero: quali sono i confini, perché sono stati scelti e cosa deve restare vero.

Come la legge di Conway influenza la mia architettura?

La legge di Conway afferma che i sistemi finiscono strutturati come i pattern di comunicazione dell'organizzazione che li costruisce, perché le interfacce tra i team diventano interfacce nel software. La conseguenza pratica è che non puoi imporre un'architettura che combatte il tuo organigramma aspettandoti che regga; i confini andranno alla deriva per corrispondere a come i team comunicano davvero. L'"Inverse Conway Manoeuvre" volge ciò a tuo vantaggio: plasma i team attorno ai confini che vuoi nel software - per esempio un team per bounded context - così che l'architettura desiderata sia il percorso di minore resistenza. Architettura e progettazione dei team sono quindi due facce della stessa decisione.

Cos'è il debito tecnico e come lo gestisco a livello architetturale?

Il debito tecnico è il costo futuro implicito di una scelta opportunistica rispetto a una più solida ma più lenta; accumula "interessi" perché la scorciatoia rende più difficili i cambiamenti futuri. La distinzione chiave è tra debito deliberato e registrato, preso come compromesso consapevole, e debito incosciente e nascosto, che degrada silenziosamente il sistema. Gestiscilo rendendolo visibile: registralo (spesso come ADR o un registro del debito), stima il suo costo di mantenimento e ripagalo opportunisticamente quando tocchi di nuovo l'area interessata. Il debito architetturale è il tipo più pericoloso perché, a differenza di un code smell locale, è intrecciato in confini e contratti costosi da cambiare.