Architettura
L'architettura del software è l'insieme delle decisioni di progetto significative sulla struttura di un sistema: gli elementi che lo compongono, le loro relazioni e le proprietà di entrambi. È definita tanto dai vincoli che impone quanto dalla libertà che concede. Secondo la ISO/IEC/IEEE 42010, un'architettura si esprime attraverso viste che indirizzano le esigenze di stakeholder diversi.
Stile architetturale
Uno stile architetturale è uno schema riutilizzabile e con un nome di organizzazione del sistema che definisce i tipi di elementi e le regole che ne governano l'interazione, come layered, microservizi, event-driven o pipe-and-filter. Uno stile è più ampio di un design pattern: plasma l'intero sistema anziché un problema localizzato. Scegliere uno stile vincola il sistema a uno specifico insieme di compromessi sugli attributi di qualità.
Architecture Decision Record (ADR)
Un ADR è un breve documento che cattura una singola decisione architetturale significativa, il suo contesto, le opzioni considerate, la decisione presa e le sue conseguenze. Gli ADR sono conservati insieme al codice perché la motivazione di una scelta sopravviva alle persone che l'hanno presa. Trasformano l'architettura in un registro verificabile e ad appendice anziché in conoscenza tacita.
Attributo di qualità
Un attributo di qualità è una proprietà misurabile o verificabile di un sistema che indica quanto bene esso soddisfi le esigenze degli stakeholder oltre la pura funzionalità, come prestazioni, disponibilità, scalabilità, sicurezza o modificabilità. L'architettura è la leva principale per gli attributi di qualità perché questi dipendono soprattutto dalla struttura, non dal codice locale. Si specificano con scenari concreti (stimolo, ambiente, risposta, misura della risposta) anziché con aggettivi vaghi.
Accoppiamento
L'accoppiamento è il grado di interdipendenza tra due elementi: quanto uno deve conoscere dell'altro per funzionare. Un accoppiamento lasco permette agli elementi di cambiare, essere distribuiti e ragionati in modo indipendente; un accoppiamento stretto propaga cambiamenti e guasti. L'architettura riduce l'accoppiamento tramite interfacce stabili, inversione delle dipendenze e messaggistica asincrona.
Coesione
La coesione misura quanto fortemente le responsabilità all'interno di un singolo elemento appartengano insieme. Alta coesione significa che un elemento ha una sola ragione chiara di esistere; bassa coesione mescola preoccupazioni non correlate e tende a cambiare per molte ragioni. L'obiettivo strutturale di una buona architettura è alta coesione interna ai moduli e accoppiamento lasco tra di essi.
Architettura a livelli (layered)
L'architettura a livelli (n-tier) organizza un sistema in strati orizzontali - tipicamente presentazione, applicazione, dominio e persistenza - dove ogni livello dipende solo da quello sottostante. È lo stile enterprise di default perché separa le preoccupazioni ed è facile da comprendere. I suoi rischi sono l'anti-pattern "sinkhole", in cui le richieste attraversano livelli che non aggiungono valore, e l'accoppiamento accidentale quando i livelli vengono scavalcati.
Architettura esagonale
L'architettura esagonale (Ports and Adapters), introdotta da Alistair Cockburn, isola la logica applicativa entro un confine che comunica con il mondo esterno solo attraverso le porte (interfacce) implementate dagli adattatori. Lo stesso nucleo può essere pilotato da test, da un controller web o da una CLI, e può dialogare con un database reale o con un fake in memoria. Rende l'applicazione indipendente dai meccanismi di consegna e dall'infrastruttura.
Clean Architecture
La Clean Architecture, formulata da Robert C. Martin, dispone il codice in anelli concentrici dove le dipendenze a livello di sorgente puntano solo verso l'interno, verso le regole di business aziendali e applicative. Framework, UI e database vivono nell'anello più esterno e sono trattati come plugin. La regola delle dipendenze mantiene la logica di business testabile e indipendente da scelte tecnologiche volatili.
Onion Architecture
L'Onion Architecture pone il modello di dominio al centro, circondato da servizi di dominio, servizi applicativi e infine infrastruttura e UI all'esterno, con tutte le dipendenze che puntano verso l'interno. È una stretta parente dell'architettura esagonale e della Clean Architecture, con cui condivide l'inversione delle dipendenze. L'obiettivo è proteggere il dominio dai cambiamenti infrastrutturali.
Microservizi
I microservizi strutturano un'applicazione come una suite di servizi piccoli e distribuibili in modo indipendente, ciascuno proprietario dei propri dati e in comunicazione sulla rete. Abilitano scalabilità e deployment indipendenti e permettono ai team di lavorare in autonomia, al prezzo della complessità dei sistemi distribuiti. Si adattano a organizzazioni con la maturità operativa per gestire molti servizi e a un dominio che si decompone con pulizia.
Monolite
Un monolite è un sistema distribuito come un'unica unità, con tutti i moduli che condividono un processo e di solito un database. Un "monolite modulare" ben strutturato mantiene confini interni netti evitando il sovraccarico di rete, consistenza e operatività della distribuzione. Iniziare con un monolite ed estrarre servizi solo quando emerge un'esigenza reale è un default ampiamente raccomandato.
Service-Oriented Architecture (SOA)
La SOA struttura le funzionalità come servizi a grana grossa, riutilizzabili e individuabili, che comunicano attraverso uno strato di messaggistica condiviso, storicamente un enterprise service bus. Precede i microservizi ed enfatizza il riuso e l'orchestrazione a livello aziendale. I microservizi sono spesso descritti come un'evoluzione più a grana fine e decentralizzata della SOA, senza bus centrale.
Architettura event-driven
L'architettura event-driven compone un sistema da produttori che emettono eventi e consumatori che vi reagiscono, disaccoppiati tramite un broker o un log. Abilita accoppiamento lasco, elaborazione asincrona e scalabilità, ma rinuncia alla semplicità del request/response sincrono. Ragionare sul flusso end-to-end e sull'ordinamento diventa più difficile e richiede correlazione e tracing.
Event sourcing
L'event sourcing persiste l'intera sequenza degli eventi che cambiano lo stato come fonte di verità, anziché il solo stato corrente. Lo stato corrente è derivato rieseguendo gli eventi, il che produce una traccia di audit completa e la capacità di ricostruire le proiezioni. I suoi costi sono l'evoluzione dello schema degli eventi, la consistenza eventuale dei modelli di lettura e una maggiore complessità concettuale.
CQRS
La Command Query Responsibility Segregation separa il modello che modifica lo stato (i comandi) da quello che lo legge (le query), permettendo di ottimizzare e scalare ciascun lato in modo indipendente. È spesso abbinata all'event sourcing e alle proiezioni lato lettura. CQRS aggiunge complessità ed è giustificata solo quando i carichi o i modelli di lettura e scrittura divergono davvero.
Domain-Driven Design (DDD)
Il Domain-Driven Design è un approccio che allinea il modello software al dominio di business attraverso un linguaggio ubiquo condiviso e la modellazione esplicita di bounded context, aggregati ed eventi di dominio. I suoi pattern strategici plasmano i confini del sistema; quelli tattici plasmano il modello all'interno di un confine. Il DDD è più prezioso nei domini complessi, dove la parte difficile è il modello e non la tecnologia.
Bounded context
Un bounded context è un confine esplicito entro il quale un particolare modello di dominio e il suo linguaggio ubiquo sono internamente consistenti. Lo stesso termine può legittimamente significare cose diverse in contesti diversi, e l'integrazione avviene tramite contratti ben definiti come una context map. È il pattern strategico centrale del DDD e un candidato naturale come confine di un servizio.
Aggregato
Un aggregato è un cluster di oggetti di dominio trattato come una singola unità per le modifiche ai dati, accessibile solo attraverso la sua aggregate root, che ne garantisce gli invarianti. Ogni transazione dovrebbe modificare un singolo aggregato per mantenere chiari i confini di consistenza. Aggregati piccoli riducono la contesa e si mappano in modo pulito su una singola transazione di database.
API Gateway
Un API gateway è un punto di ingresso unico che si frappone davanti ai servizi di backend e gestisce preoccupazioni trasversali come routing, autenticazione, rate limiting, aggregazione di richieste e traduzione di protocollo. Protegge i client dalla topologia interna dei servizi e permette ai servizi di evolvere dietro una facciata stabile. Sovraccaricarlo di logica di business lo trasforma in un collo di bottiglia che genera accoppiamento.
Backend for Frontend (BFF)
Un Backend for Frontend è un gateway dedicato, ritagliato sulle esigenze di una specifica esperienza client, come un'app web o un'app mobile. Ogni BFF aggrega e modella i dati per il suo consumatore, evitando un unico gateway generico che debba soddisfare ogni client. Il pattern baratta una certa duplicazione con una proprietà più chiara e un'ottimizzazione specifica per il client.
Service mesh
Una service mesh è uno strato infrastrutturale che gestisce le preoccupazioni della comunicazione servizio-a-servizio - TLS reciproco, retry, timeout, bilanciamento del carico e osservabilità - tramite proxy sidecar, in modo trasparente al codice applicativo. Sposta le policy di resilienza e sicurezza fuori da ogni servizio e dentro un piano di controllo uniforme. Il suo costo è la complessità operativa e la latenza per ogni hop.
Saga
Una saga gestisce una transazione di business che attraversa più servizi come una sequenza di transazioni locali, ciascuna con un'azione compensativa che la annulla se un passo successivo fallisce. Sostituisce il commit distribuito a due fasi, che non scala tra servizi autonomi. Le saga esistono nelle varianti a orchestrazione (un coordinatore guida i passi) e a coreografia (i servizi reagiscono agli eventi).
Consistenza eventuale
La consistenza eventuale è una garanzia per cui, in assenza di nuovi aggiornamenti, tutte le repliche di un dato convergeranno nel tempo allo stesso valore, senza garantire una consistenza immediata. È il modello di consistenza che la maggior parte dei sistemi distribuiti e a microservizi accetta in cambio di disponibilità e tolleranza alle partizioni. I progetti devono rendere visibile e tollerare la finestra di inconsistenza anziché nasconderla.
Teorema CAP
Il teorema CAP afferma che un archivio dati distribuito può fornire contemporaneamente al massimo due tra Consistenza, Disponibilità (Availability) e Tolleranza alle partizioni. Poiché le partizioni di rete sono inevitabili nella pratica, i sistemi reali scelgono di fatto tra consistenza e disponibilità quando si verifica una partizione. Inquadra un compromesso fondamentale ma è una semplificazione della più ricca formulazione PACELC.
Idempotenza
Un'operazione è idempotente se eseguirla più volte ha lo stesso effetto di eseguirla una sola volta. L'idempotenza rende sicuri i retry nei sistemi distribuiti, il che è essenziale perché le reti garantiscono molto più facilmente la consegna "at least once" che la "exactly once". Si implementa comunemente con chiavi di idempotenza su cui il ricevente deduplica.
Resilienza
La resilienza è la capacità di un sistema di continuare a fornire un servizio accettabile di fronte a guasti, dipendenze degradate e picchi di carico. Si progetta con pattern come timeout, retry con backoff, circuit breaker, bulkhead e degradazione controllata. L'obiettivo è contenere il guasto anziché presumere che non accada mai.
Circuit breaker
Un circuit breaker è un pattern di resilienza che interrompe le chiamate verso una dipendenza in errore una volta superata una soglia di errori, fallendo rapidamente anziché esaurire i thread su chiamate destinate al timeout. Dopo un periodo di raffreddamento consente chiamate di prova (stato half-open) per testare il recupero prima di richiudersi. Impedisce che una singola dipendenza lenta si propaghi in un'interruzione dell'intero sistema.
Bulkhead
Il pattern bulkhead isola le risorse - come pool di thread o pool di connessioni - per ciascuna dipendenza, così che il guasto o la saturazione di una non possa consumare la capacità necessaria alle altre. Il nome deriva dai compartimenti stagni di una nave, che impediscono a una sezione allagata di affondare l'intera imbarcazione. Limita il raggio d'azione di una dipendenza malfunzionante.
Scalabilità
La scalabilità è la capacità di un sistema di gestire un carico crescente aggiungendo risorse. Lo scaling verticale aggiunge capacità a un singolo nodo; quello orizzontale aggiunge più nodi ed è la base dell'elasticità del cloud. Servizi stateless, dati partizionati ed elaborazione asincrona sono gli abilitatori strutturali dello scaling orizzontale.
Disponibilità
La disponibilità è la quota di tempo in cui un sistema è operativo e in grado di servire richieste, di solito espressa in "nove" (per esempio 99,9%). Si migliora con ridondanza, failover, health check ed eliminazione dei punti singoli di guasto. È esplicitamente barattata con la consistenza secondo il teorema CAP durante le partizioni.
Tolleranza ai guasti
La tolleranza ai guasti è la proprietà di continuare a operare correttamente nonostante il fallimento di alcuni componenti. Si ottiene tramite ridondanza, replicazione e failover automatico, così che nessun singolo guasto metta giù il sistema. Si distingue dalla resilienza per l'enfasi sul mascherare i guasti piuttosto che degradare con grazia.
Strangler Fig
Il pattern Strangler Fig sostituisce in modo incrementale un sistema legacy instradando le funzionalità verso un nuovo sistema, funzionalità per funzionalità, finché il vecchio sistema può essere ritirato. Il nuovo comportamento cresce attorno al legacy come un fico strangolatore attorno a un albero. Riduce il rischio delle riscritture evitando un'unica migrazione "big bang".
Anti-corruption layer
Un anti-corruption layer è un confine di traduzione che protegge un bounded context dal modello di un sistema esterno o legacy, convertendo tra i due affinché i concetti estranei non trapelino all'interno. Mantiene pulito il modello interno quando si integra con sistemi che non si controllano. È un pattern DDD chiave per un'integrazione sicura.
Sidecar
Il pattern sidecar distribuisce un componente di supporto accanto a un'applicazione primaria nella stessa unità (per esempio un pod), estendendola con capacità come logging, configurazione o proxying senza modificarla. I data plane delle service mesh sono implementati come sidecar. Abilita il riuso poliglotta di funzionalità trasversali.
Twelve-Factor App
La Twelve-Factor App è una metodologia di dodici principi per costruire servizi portabili e scalabili adatti alle piattaforme cloud, che copre la configurazione nell'ambiente, i processi stateless, le dipendenze esplicite e la disponibilità all'eliminazione. Codifica pratiche che rendono le applicazioni facili da distribuire e scalare orizzontalmente. Ha influenzato fortemente l'architettura cloud-native moderna.
Cloud-native
Cloud-native descrive sistemi progettati per sfruttare l'elasticità, i servizi gestiti e l'automazione delle piattaforme cloud, tipicamente usando container, orchestrazione, microservizi e infrastruttura dichiarativa. L'enfasi è su resilienza, scalabilità e consegna rapida e automatizzata. La CNCF guida buona parte dell'ecosistema circostante.
Legge di Conway
La legge di Conway osserva che le organizzazioni progettano sistemi la cui struttura rispecchia la loro stessa struttura di comunicazione. Implica che i confini dei team tendono a diventare confini architetturali, nel bene e nel male. L'"Inverse Conway Manoeuvre" plasma deliberatamente i team per produrre l'architettura desiderata.
Valutazione dell'architettura (ATAM)
L'Architecture Tradeoff Analysis Method è una tecnica strutturata, basata su scenari, del Software Engineering Institute per valutare un'architettura rispetto ai suoi requisiti di attributi di qualità prima della sua realizzazione. Elicita scenari, identifica gli approcci architetturali e fa emergere punti di sensibilità, compromessi e rischi. Trasforma la revisione architetturale in un'attività ripetibile e basata su evidenze.
Fitness function
Una fitness function architetturale è un test automatico e oggettivo di una caratteristica architetturale desiderata - come una regola di stratificazione, un budget di latenza o un vincolo di dipendenza - che fa fallire la build quando la proprietà è violata. È il meccanismo centrale dell'architettura evolutiva, che rende l'integrità architetturale continuamente verificabile. Strumenti come ArchUnit esprimono tali regole nel codice.
Architettura evolutiva
Un'architettura evolutiva è progettata per supportare un cambiamento guidato e incrementale lungo molteplici dimensioni, protetta da fitness function anziché congelata a monte. Tratta il cambiamento come atteso e l'architettura come un vincolo vivo continuamente validato. L'approccio rifiuta l'idea che un'architettura perfetta possa essere specificata una volta per tutte all'inizio.
Principio di inversione delle dipendenze
Il principio di inversione delle dipendenze afferma che i moduli di alto livello non dovrebbero dipendere da quelli di basso livello; entrambi dovrebbero dipendere da astrazioni, e le astrazioni non dovrebbero dipendere dai dettagli. È il meccanismo che permette alla logica di dominio di restare indipendente da framework e database. È il fondamento strutturale delle architetture esagonale, onion e clean.
Separazione delle preoccupazioni
La separazione delle preoccupazioni è il principio di suddividere un sistema affinché ogni parte indirizzi una preoccupazione distinta con sovrapposizione minima. È alla base della stratificazione, della modularità e del principio di responsabilità singola, ed è l'arma principale contro la complessità accidentale. Preoccupazioni separate con pulizia possono essere cambiate, testate e comprese in isolamento.
Deriva architetturale
La deriva architetturale è la graduale divergenza tra l'architettura intesa e il sistema effettivamente costruito, causata da cambiamenti opportunistici che erodono i confini. Se non controllata, diventa erosione architetturale, in cui la struttura originale non è più riconoscibile. Fitness function e controlli di dipendenza sono usati per rilevarla e prevenirla.
Debito tecnico
Il debito tecnico è il costo futuro implicito nello scegliere ora una soluzione opportunistica invece di una migliore ma più lenta. Come il debito finanziario, accumula "interessi" man mano che la scorciatoia rende più difficili i cambiamenti futuri. Il debito deliberato e registrato può essere un compromesso valido; quello incosciente e nascosto degrada silenziosamente l'architettura.
Modello di lettura (proiezione)
Un modello di lettura, o proiezione, è una rappresentazione dei dati denormalizzata e ottimizzata per le query, costruita specificamente per servire le letture e tenuta separata dal modello di scrittura. È centrale nei sistemi CQRS e a event sourcing, dove le proiezioni sono ricostruite rieseguendo gli eventi. Disaccoppia le prestazioni delle query dai vincoli dello schema transazionale di scrittura.
Modello C4
Il modello C4 è una tecnica leggera per visualizzare l'architettura del software a quattro livelli di astrazione - System Context, Containers, Components e Code - così che ogni diagramma si rivolga a uno specifico pubblico. Predilige una notazione piccola e coerente rispetto al formalismo pesante. È ampiamente usato per mantenere i diagrammi architetturali comprensibili e manutenibili.