Pattern architetturali

Pattern architetturali del software 🧱

La buona architettura non fa miracoli, ma evita i disastri. Il che, in produzione, vale quanto un miracolo ben riuscito.

In questo documento trovi un elenco ragionato dei principali pattern architetturali e di design, suddivisi in macro categorie. L’obiettivo è darti un lessico condiviso e una mappa pratica per scegliere cosa usare, quando e perché — senza innamorarsi della buzzword del momento.

Nota di stile: nomi “storici” come master/slave li troverai aggiornati a terminologia più corretta (es. primario/replica). I diagrammi li lasciamo per la lavagna; qui andiamo dritti al punto.

Architetture applicative (strutturali) 🧩

Pattern che definiscono la struttura interna di un’applicazione e la separazione delle responsabilità.

Layered (n‑tier) 🧩

Organizza l’app in livelli (presentazione, applicativo, dominio, persistenza) con dipendenze unidirezionali verso il basso.

  • Pro: struttura chiara, testabilità elevata, onboarding facile, riuso per layer.
  • Contro: rigidità tra layer, passaggi “a cascata” che aggiungono latenza; tentazione di “bucare i layer”.
  • Esempio: app enterprise CRUD con validazioni e regole moderate.
🎯 Quando usarlo
Domini semplici o medi; necessità di onboarding rapido e separazione netta tra layer.
⚠️ Attenzioni
Evita di saltare i layer; definisci contratti e DTO. Misura l'impatto di passaggi multipli tra layer.

MVC (model‑view‑controller) 🧩

Separa la gestione dello stato (Model), la presentazione (View) e il flusso/interazioni (Controller) — classico nelle web UI.

  • Pro: separazione delle responsabilità, testabilità delle viste e dei controller, parallelismo tra ruoli.
  • Contro: può risultare verboso; in domini semplici rischia di aggiungere strati inutili.
  • Esempio: e‑commerce con viste catalogo/carrello e controller per azioni utente.
🎯 Quando usarlo
Interfacce ricche, SSR o SPA con framework; separazione netta tra presentazione e logica.
⚠️ Attenzioni
Evita controller grassi; mantieni la logica nel dominio; testa viste e controller in isolamento.

Hexagonal (ports & adapters) 🧩

Metti il dominio al centro; tutto ciò che è I/O (DB, API, code, UI) si collega via “porte” (interfacce) e “adattatori”.

  • Pro: indipendenza tecnologica, testabilità eccellente, sostituibilità degli adapter.
  • Contro: più astrazioni da gestire, curva iniziale.
  • Esempio: core pagamenti con adapter multipli per PSP diversi.
🎯 Quando usarlo
Quando prevedi cambi di tecnologia (DB, broker, PSP) o vuoi testare il dominio in isolamento.
⚠️ Attenzioni
Attenzione al leakage di dettagli infrastrutturali nel dominio; mantieni porte minimali e stabili.

Microkernel (plug‑in) 🧩

Un nucleo minimale fornisce servizi base estendibili tramite plugin indipendenti.

  • Pro: estendibilità, sperimentazione rapida, isolabilità dei plugin.
  • Contro: gestione del ciclo di vita plugin, compatibilità API del core.
  • Esempio: motore di regole o scheduler con job plugin.
🎯 Quando usarlo
Core stabile e funzionalità estese da plugin; necessità di abilitare/aggiornare capability ai bordi.
⚠️ Attenzioni
Compatibilità dell’API del core, versionamento dei plugin, gestione del ciclo di vita e isolamento.

Pipes & filters 🧩

Catena di filtri indipendenti che trasformano dati lungo una pipeline.

  • Pro: composizione, riuso dei filtri, parallelizzazione.
  • Contro: overhead di serializzazione tra filtri, colli di bottiglia.
  • Esempio: pipeline log → parse → enrich → route → store.
🎯 Quando usarlo
ETL, trasformazioni a stadi, streaming con step indipendenti e riusabili.
⚠️ Attenzioni
Back‑pressure, parallelizzazione, colli di bottiglia, serializzazione tra step.

Monolite modulare 🧩

Un’unica codebase/processo organizzato in moduli ben isolati con confini espliciti e dipendenze controllate.

  • Pro: semplicità operativa, transazioni locali, deploy unificato.
  • Contro: rischi di accoppiamento se i confini non sono rispettati; scala solo fino a un certo punto.
  • Esempio: applicazione business con moduli per ordini, fatture, magazzino e report.
🎯 Quando usarlo
Team piccolo/medio, domain boundaries chiari ma non serve distribuzione; vuoi evitare il monolite caotico e rimandare i microservizi.
⚠️ Attenzioni
Enforza confini modulari (linter/arch rules), API interne tra moduli, dipendenze unidirezionali, contratti stabili.

Onion / Clean architecture 🧩

Varianti che, come l’esagonale, mantengono il dominio al centro stratificando dipendenze verso l’esterno.

  • Pro: testabilità, indipendenza dai framework, facilità di sostituzione dell’infrastruttura.
  • Contro: più astrazioni, curva di adozione.
  • Esempio: servizio core con use case nel layer application e interfacce di repository al bordo.
🎯 Quando usarlo
Dominio centrale forte; vuoi ridurre il lock‑in da framework e rendere il core testabile senza I/O.
⚠️ Attenzioni
Mantieni le dipendenze sempre verso l’esterno; non far trapelare tipi di libreria nel dominio.

MVVM (model‑view‑viewmodel) 🧩

Pattern UI che separa la View dalla logica di presentazione (ViewModel). Il ViewModel media e adatta il Model per la View, esponendo stato e comportamenti; ideale per il data‑binding.

  • Pro: testabilità della logica di presentazione; riuso e binding.
  • Contro: complessità se il binding non è gestito con disciplina.
  • Esempio: app mobile/desktop con binding reattivo tra View e ViewModel.
🎯 Quando usarlo
UI reattive con binding; necessità di testare la logica di presentazione senza la View.
⚠️ Attenzioni
Limitare la logica nella View; evitare ViewModel ‘onniscienti’; gestire cicli vita/binding.

Architetture distribuite e di integrazione 🌐

Pattern che organizzano sistemi composti da più componenti/servizi e il loro dialogo.

Microservizi 🌐

App composta da servizi piccoli, autonomi e indipendentemente distribuibili, idealmente ciascuno proprietario dei propri dati.

  • Pro: scalabilità mirata, velocità di rilascio per team, resilienza ai guasti locali.
  • Contro: transazioni distribuite, osservabilità, costi operativi più alti.
  • Esempio: e‑commerce con servizi separati per catalogo, ordini, pagamenti, raccomandazioni.
🎯 Quando usarlo
Organizzazione per team autonomi, domini ampi con esigenze di scalabilità differenziata.
⚠️ Attenzioni
Osservabilità end‑to‑end, contract testing, gestione della coerenza (saghe/outbox), governance API; evita DB condivisi e, in transizione, pianifica la separazione graduale dei dati.

SOA (service‑oriented) 🌐

Servizi più “coarse‑grained” e standardizzati; spesso orchestrati, storicamente con ESB, oggi più leggeri.

  • Pro: contratti stabili, composizione di servizi, governance.
  • Contro: rischio “bus” centrale come collo di bottiglia; rigidità.
  • Esempio: servizi aziendali di anagrafica, fatturazione, reporting condivisi da più applicazioni.
🎯 Quando usarlo
Integrazione enterprise con contratti stabili e riuso trasversale.
⚠️ Attenzioni
Evitare bus centralizzati come single bottleneck; versioning dei contratti; monitoraggio.

Event‑driven 🌐

Comunicazione asincrona basata su eventi prodotti e consumati da componenti disaccoppiati.

  • Pro: decoupling, scalabilità, estendibilità (nuovi consumer senza toccare i producer).
  • Contro: ordering e deduplica, schema evolution, consistenza eventuale.
  • Esempio: ordini→evento “OrderPlaced”→notifiche/spedizioni/analytics.
🎯 Quando usarlo
Integrazione debole tra domini, real‑time, fan‑out, reattività.
⚠️ Attenzioni
Schema evolution, ordering/partizionamento, idempotenza, DLQ, tracciabilità.

Broker 🌐

Un mediatore gestisce routing, trasformazioni e invocazioni remote tra componenti.

  • Pro: decoupling, osservabilità centralizzata.
  • Contro: latenza, punto di contenzione, lock‑in.
  • Esempio: integrare sistemi legacy via message broker con trasformazioni di schema.
🎯 Quando usarlo
Eterogeneità tecnologica, necessità di routing/trasformazioni centralizzate.
⚠️ Attenzioni
Non introdurre logica di business nel broker; evitare lock‑in; attenzione alla latenza.

Pub/Sub 🌐

Produttori pubblicano su topic, consumatori si sottoscrivono e ricevono eventi.

  • Pro: aggiunta di consumer senza impatti, scalabilità orizzontale.
  • Contro: test end‑to‑end, gestione ordering/partizionamento.
  • Esempio: invio notifiche push ed email a partire da un unico evento.
🎯 Quando usarlo
Fan‑out di eventi, notifiche, stream multi‑consumer.
⚠️ Attenzioni
Semantica di consegna (at‑least/exactly‑once), ordering/partizionamento, test end‑to‑end.

API gateway 🌐

Punto d’ingresso unico che fornisce routing, auth, rate limiting, aggregazione e osservabilità verso servizi a valle.

  • Pro: semplifica i client, centralizza policy e sicurezza.
  • Contro: rischio di collo di bottiglia; configurazioni complesse.
  • Esempio: gateway che espone endpoint pubblici e smista verso microservizi interni.
🎯 Quando usarlo
Molti servizi interni; necessità di controlli trasversali (auth, quota, logging).
⚠️ Attenzioni
Alta disponibilità del gateway, rollback config, performance e cold start (se serverless).

Backend for Frontend (BFF) 🌐

Un backend dedicato per ciascun tipo di front‑end (web, mobile) per adattare API, performance e aggregazioni.

  • Pro: API su misura per esperienza utente; riduce over/under‑fetching.
  • Contro: più artefatti da mantenere; rischio duplicazioni.
  • Esempio: BFF mobile con risposte aggregate e payload minimizzati.
🎯 Quando usarlo
Client differenti (web/mobile) con esigenze diverse; necessità di ottimizzare UX e payload.
⚠️ Attenzioni
Allineamento con i team frontend; condividere contratti e policy comuni; evitare business logic nei BFF.

Service mesh / sidecar 🌐

Strato infrastrutturale (es. Envoy/istio) che sposta in sidecar networking, mTLS, retry, circuit breaker, osservabilità.

  • Pro: policy consistenti senza toccare il codice; telemetria ricca.
  • Contro: complessità operativa, consumo risorse.
  • Esempio: microservizi con proxy sidecar per mTLS e controllo traffico.
🎯 Quando usarlo
Molti servizi, necessità di sicurezza e controllo traffico coerenti e osservabilità avanzata.
⚠️ Attenzioni
Gestione versioni del mesh, costi overhead, formazione SRE; la logica applicativa deve gestire gli errori di business non interpretabili dal mesh (evita duplicare retry/breaker se già nel mesh).

Service discovery 🌐

Risoluzione dinamica degli endpoint dei servizi (DNS/registry) per permettere a client di trovare istanze attive.

  • Pro: resilienza a cambi/scale; niente endpoint hardcoded.
  • Contro: complessità di health check e caching.
  • Esempio: client che risolvono istanze via consul/eureka/DNS.
🎯 Quando usarlo
Ambienti dinamici (Kubernetes, cloud) con scaling/rolling update frequenti.
⚠️ Attenzioni
Caching/TTL e retry; health check accurati; timeouts coerenti con SLA.

Serverless (FaaS/Event‑driven) 🌐

Funzioni gestite dal provider, attivate da eventi/HTTP, con scalabilità automatica e billing a consumo.

  • Pro: time‑to‑market rapido, scalabilità automatica, costi variabili.
  • Contro: cold start, limiti runtime, osservabilità e local dev più difficili.
  • Esempio: funzione che processa eventi da coda e aggiorna un datastore.
🎯 Quando usarlo
Lavori bursty/event‑driven, integrazioni leggere, backend leggeri senza server da gestire.
⚠️ Attenzioni
Cold start (mitigabili con provisioned concurrency/warm‑up), idempotenza, timeouts/limiti, lock‑in verso servizi gestiti; considera cache read‑through/edge per ridurre latenza.

Client‑server 🌐

Client che richiede risorse/servizi, server che elabora e risponde.

  • Pro: centralizzazione dei dati, sicurezza semplificata.
  • Contro: single point of failure se non ridondato.
  • Esempio: app mobile che interagisce con API REST.
🎯 Quando usarlo
Pattern base per web/mobile e servizi centralizzati.
⚠️ Attenzioni
Ridondanza del server, statelessness, caching e rate limiting dove necessario.

P2P (peer‑to‑peer) 🌐

Nodi paritari senza centro unico, ognuno può essere client e server.

  • Pro: assenza di SPOF, scalabilità con i nodi.
  • Contro: sicurezza e qualità del servizio variabili.
  • Esempio: file sharing o edge sync.
🎯 Quando usarlo
Distribuzione contenuti, sincronizzazione decentralizzata, reti resilienti.
⚠️ Attenzioni
Sicurezza end‑to‑end, QoS variabile, NAT traversal (spesso gestito da WebRTC/libp2p), reputazione dei nodi.

Controller‑responder 🌐

Un controller coordina operazioni di write; nodi responder servono letture/repliche.

  • Pro: isolamento del carico di lettura, riduzione contesa.
  • Contro: sincronizzazione e lag tra copie.
  • Esempio: API che legge da replica e scrive sul primario.
🎯 Quando usarlo
Workload con letture >> scritture; necessità di isolare il read path.
⚠️ Attenzioni
Lag tra copie, routing delle query, invalidazione cache, politiche di consistenza.

Dati, query e persistenza 💾

Pattern focalizzati su come modellare accesso e gestione dei dati.

CQRS 💾

Separazione netta tra il modello di scrittura (command) e quello di lettura (query) per ottimizzarli indipendentemente. Non implica necessariamente database separati: la separazione può essere solo logica.

  • Pro: performance in lettura, modelli dedicati, scala indipendente.
  • Contro: coerenza eventuale, maggiore complessità.
  • Esempio: read model denormalizzato per dashboard analitiche.
🎯 Quando usarlo
Molte letture, query complesse, esigenze di proiezioni ottimizzate.
⚠️ Attenzioni
Gestisci coerenza eventuale, proiezioni idempotenti, sincronizzazione e recovery.

Event sourcing 💾

Lo stato deriva dalla sequenza di eventi append‑only; si ricostruisce “riproducendoli”.

  • Pro: tracciabilità totale, time‑travel, possibilità di nuove proiezioni.
  • Contro: migrazione eventi, evoluzione schemi, storage crescente.
  • Esempio: contabilità, ordini con timeline completa.
🎯 Quando usarlo
Audit forte, necessità di time‑travel, regole che evolvono.
⚠️ Attenzioni
Versiona eventi, snapshot, piani di migrazione per cambi schema; privacy/GDPR: prevedi redaction o cifratura selettiva.

Repository 💾

Astrazione dell’accesso ai dati dietro interfacce del dominio.

  • Pro: testabilità, separazione di responsabilità.
  • Contro: rischio di coprire feature specifiche del datastore utili.
  • Esempio: OrderRepository.findOpenByCustomerId(...).
🎯 Quando usarlo
Test del dominio senza dipendenze infrastrutturali; separazione persistenza/domino.
⚠️ Attenzioni
Evita repository generici eccessivi; esponi query di dominio significative; non nascondere feature utili del datastore.

Sharding 💾

Partizionamento dei dati su più nodi in base a una chiave (es. customerId).

  • Pro: scalabilità orizzontale, isolamento del carico.
  • Contro: hotspot se la chiave è sbilanciata, complessità operativa.
  • Esempio: multi‑tenant con shard per cliente/regione.
🎯 Quando usarlo
Dataset molto grandi o throughput elevato che richiedono scalabilità orizzontale.
⚠️ Attenzioni
Scelta della chiave di partizione; prevenire hotspot; piani di rebalance e migrazione.

Primario/replica (replicazione) 💾

Scritture su primario, letture su repliche asincrone/sincrone.

  • Pro: offload del read path, failover.
  • Contro: lag, inconsistenze temporanee.
  • Esempio: reporting su replica, operazioni critiche sul primario.
🎯 Quando usarlo
Scalare letture e garantire HA con failover.
⚠️ Attenzioni
Lag tra repliche, routing consapevole, coerenza per route, split‑brain in failover.

Static content hosting 💾

Separazione e consegna di asset statici via CDN/edge rispetto al contenuto dinamico.

  • Pro: latenza bassa, costi ridotti sul backend.
  • Contro: invalidazione e coerenza cache.
  • Esempio: immagini, CSS/JS su CDN; API dinamiche dal backend.
🎯 Quando usarlo
Web ad alto traffico, media e asset statici serviti da edge/CDN.
⚠️ Attenzioni
Invalidazione cache, TTL, hashing degli asset, differenze tra ambienti.

Cache‑aside 💾

Il codice applicativo legge prima dalla cache e, in caso di miss, carica dal database popolando la cache.

  • Pro: semplice, riduce latenza e carico sul DB.
  • Contro: invalidazione complessa, rischio di dati stantii.
  • Esempio: lookup di profili utente con TTL e invalidazione su update.
🎯 Quando usarlo
Dati letti spesso e aggiornati raramente; ridurre latenza e costi DB.
⚠️ Attenzioni
Strategie di invalidazione, TTL adeguati, coerenza in presenza di update concorrenti.

Read‑through cache 💾

La cache intercetta le letture e, in caso di miss, carica automaticamente dal datastore popolandosi (trasparente al chiamante).

  • Pro: semplicità lato applicativo; warm automatico; riduce la duplicazione di logica di loading.
  • Contro: controllo minore sull’invalidazione; rischio di stampede senza protezioni; dipendenza dalle capacità del cache layer.
  • Esempio: catalogo prodotti che la cache recupera automaticamente dal DB al primo accesso.
🎯 Quando usarlo
Dati molto riletti e poco aggiornati; preferisci delegare il caricamento al layer cache.
⚠️ Attenzioni
TTL coerenti, stampede protection (locking/batching), coerenza post‑write e invalidazioni mirate.

Outbox (transactional outbox) 💾

Scrivi eventi in una tabella “outbox” nella stessa transazione del write; un processo li pubblica in modo affidabile.

  • Pro: garantisce pubblicazione affidabile di eventi evitando perdite/duplicati.
  • Contro: processo di dispatch da gestire; ordering per aggregato.
  • Esempio: al salvataggio di un ordine, persisti l’evento in outbox e pubblica su broker.
🎯 Quando usarlo
Integrazione eventi affidabile fra DB e broker; necessità di idempotenza/consistenza.
⚠️ Attenzioni
Idempotency keys, cleanup dell’outbox, tracing tra write ed evento.

Lambda / Kappa architecture 💾

Architetture dati per analytics: Lambda combina batch+speed layer; Kappa usa solo stream per semplificare. Nota: sono architetture di sistema più che pattern di sola persistenza.

  • Pro: scalano su grandi volumi, consentono elaborazioni real‑time.
  • Contro: complessità infrastrutturale (Lambda), semantiche di stream (Kappa).
  • Esempio: pipeline eventi con proiezioni real‑time e calcoli batch di consolidamento.
🎯 Quando usarlo
Analytics su grandi volumi, insight near real‑time e consolidamenti periodici.
⚠️ Attenzioni
Gestione schemi, latenze diverse tra layer, esecuzioni idempotenti, costi operativi.

Affidabilità, resilienza e controllo del traffico 🛡️

Pattern che aiutano a non far cadere tutto quando qualcosa va storto (perché succederà).

Circuit breaker 🛡️

Interrompe chiamate verso dipendenze degradate/apparse fallimentari per evitare valanghe di errori.

  • Pro: stabilizza il sistema, protegge le risorse.
  • Contro: gestione stati (open/half‑open/closed), tuning non banale.
  • Esempio: degrado di funzionalità “non essenziali” quando un servizio è giù.
🎯 Quando usarlo
Dipendenze non affidabili o sensibili alla latenza.
⚠️ Attenzioni
Timeout, retry con jitter/backoff, fallback chiari e monitorati.

Bulkhead (paratie stagne) 🛡️

Isola risorse (thread pool/connessioni) per componenti diversi, impedendo che un guasto saturi l’intero sistema.

  • Pro: confinamento dei guasti e protezione delle risorse.
  • Contro: tuning dei limiti e frammentazione delle risorse.
  • Esempio: pool separati per chiamate a servizi A/B.
🎯 Quando usarlo
Dipendenze con profili di latenza diversi; prevenire failure cascading per saturazione.
⚠️ Attenzioni
Dimensionamento pool, metriche e allarmi; combinare con circuit breaker e timeouts.

Queue‑based load leveling 🛡️

Metti in coda il lavoro per assorbire picchi, con consumer che processano al ritmo sostenibile.

  • Pro: livella picchi, protegge i backend, aumenta resilienza.
  • Contro: latenza aggiunta, gestione DLQ e ordering.
  • Esempio: coda per processare ordini invece di chiamare direttamente il servizio lento.
🎯 Quando usarlo
Picchi imprevedibili verso servizi lenti/costosi; necessità di smoothing del carico.
⚠️ Attenzioni
Idempotenza, DLQ, visibilità del backlog, stimare throughput per dimensionare consumer.

Retry/Timeout con backoff 🛡️

Imposta timeouts e retry con backoff/jitter per gestire errori transitori senza accentuare la congestione.

  • Pro: recupero automatico da glitch, stabilità.
  • Contro: se mal configurati aggravano il problema (retry storm).
  • Esempio: chiamate HTTP con timeout brevi e retry esponenziale limitato.
🎯 Quando usarlo
Dipendenze esterne o di rete con errori transitori.
⚠️ Attenzioni
Limita i retry, usa jitter, coordina coi breaker, misura per regolare i parametri.

Saga 🛡️

Coordinazione di transazioni distribuite tramite passi locali e azioni compensative.

  • Pro: consistenza logica, scalabilità.
  • Contro: maggiore complessità, casi di errore numerosi.
  • Esempio: ordine → pagamento → riserva stock → spedizione con rollback appropriati.
🎯 Quando usarlo
Processi multi‑servizio senza 2PC; necessità di consistenza logica.
⚠️ Attenzioni
Compensazioni idempotenti, timeouts, DLQ, osservabilità del flow e retry policy.

Throttling / rate limiting 🛡️

Limitazione del traffico per proteggere risorse, SLA e costi.

  • Pro: prevedibilità dei carichi, isolamento.
  • Contro: esperienza utente impattata se aggressivo.
  • Esempio: 100 req/min per API free tier.
🎯 Quando usarlo
API pubbliche/multi‑tenant, difesa da burst e controllo dei costi.
⚠️ Attenzioni
Politiche eque (token bucket/leaky bucket), risposte 429 coerenti, telemetria e allarmi.

Scalabilità e performance ⚡

Pattern pensati per reggere carico e variazioni di traffico.

Space‑based ⚡

Distribuisce stato e carico tra unità di elaborazione stateless, con middleware che gestisce dati in memoria, messaging e scalabilità.

  • Pro: assenza di colli centrali, scalabilità lineare.
  • Contro: complessità di coerenza e test end‑to‑end.
  • Esempio: sistemi di offerte/aste con picchi improvvisi.
🎯 Quando usarlo
Carichi imprevedibili, latenza bassa, necessità di scalare in‑memory.
⚠️ Attenzioni
Coerenza e replica in‑memory, eviction policy, persistenza eventuale e recovery.

Evoluzione e modernizzazione 🔧

Strangler 🔧

Si interpone fra client e legacy con una facciata; migra funzionalità una alla volta finché il legacy “si spegne”.

  • Pro: rischio ridotto, rollback più semplici, misurabilità.
  • Contro: doppio run durante la transizione, routing complesso.
  • Esempio: migrare endpoint legacy verso servizi nuovi con proxy a routing configurabile (feature flag/gradual rollout).
🎯 Quando usarlo
Modernizzazione progressiva di monoliti mission‑critical.
⚠️ Attenzioni
Routing e osservabilità parallela, test di caratterizzazione, roll‑back plan, feature flags.

Anti‑corruption layer (ACL) 🔧

Strato che traduce/modella tra sistemi o bounded context per proteggere il tuo modello dalle “impurità” esterne.

  • Pro: isola il dominio da modelli terzi; riduce accoppiamento semantico.
  • Contro: codice di traduzione da mantenere; latenza aggiunta.
  • Esempio: adapter che mappa DTO legacy in Value Object del dominio.
🎯 Quando usarlo
Integrazione con legacy o sistemi dai contratti ‘sporchi’; proteggere il modello interno.
⚠️ Attenzioni
Tracciabilità delle trasformazioni, versioning, test di contratto e back‑compatibility.

Approcci strategici di modellazione 🎯

Domain‑Driven Design (DDD) 🎯

Approccio di modellazione che guida le scelte architetturali con linguaggio ubiquo, bounded context, aggregati e separazione del core domain.

  • Pro: allineamento col business, confini chiari, codice espressivo.
  • Contro: investimento iniziale, rischio over‑engineering su CRUD semplici.
  • Esempio: vedi anche “Domain Driven Design 🧩” in questa sezione dei docs.
🎯 Quando usarlo
Domini complessi con regole in evoluzione; allineamento stretto con il business.
⚠️ Attenzioni
Parti dallo strategic design; evita 1:1 bounded context↔microservizio; prevenire over‑engineering su CRUD.

Come scegliere (in 5 mosse) 🧭

  1. Chiarisci qualità attese: performance, scalabilità, time‑to‑market, regolatorio. No, non puoi ottimizzare tutto al massimo contemporaneamente.
  2. Valuta il dominio: complesso e in evoluzione? DDD + confini chiari; data‑intensive? CQRS/event sourcing; integrazione pesante? SOA/event‑driven.
  3. Guarda l’organizzazione: team piccoli e pochi SRE? Monolite ben fatto o modulare. Team multipli autonomi? Microservizi, ma con piattaforma e osservabilità mature.
  4. Fai i conti con l’ecosistema: cloud‑native, legacy, vendor lock‑in, skill interni. L’architettura “perfetta” che il team non sa gestire… non è perfetta.
  5. Procedi in modo incrementale: feature flag, strangler, misure oggettive. Cambiare rotta presto costa poco; tardi, costa.

Mappa rapida d’uso 👇

  • Vuoi partire veloce su un dominio non complicato? Layered/MVC + repository.
  • Tante letture e query difficili? CQRS (+ proiezioni ad‑hoc).
  • Reattività tra servizi eterogenei? Event‑driven + pub/sub.
  • Domini ad alto valore e regole toste? DDD + hexagonal.
  • Modernizzi un legacy mission‑critical? Strangler + test di caratterizzazione.
  • Picchi imprevedibili? Space‑based + caching/edge.

Confronto sintetico tra pattern 🧪

Di seguito una tabella compatta per alcuni pattern chiave. Le valutazioni sono indicative (basso/medio/alto) e vanno sempre contestualizzate.

Pattern Scalabilità Complessità Team fit
Layered Medio Bassa Team piccoli/eterogenei
MVC Medio Media Team con competenze UI/BE separate
Hexagonal Medio Media Team orientati al dominio/test
Microkernel Medio Media Team che gestiscono plugin/estensioni
Pipes & Filters Alto Media Team data/processing
Microservizi Alto Alta Team multipli autonomi + SRE/Platform
SOA Medio Media Enterprise con governance forte
Event‑driven Alto Alta Team con skill messaggistica/stream
Pub/Sub Alto Media Team integrazione/real‑time
Client‑server Medio Bassa Team generalisti
P2P Alto Alta Team con esperienza reti/distribuito
API gateway N/D Media Team BE/Platform
BFF Medio Media Team FE/BE collaborativi
Service mesh/sidecar N/D Alta Team SRE/Platform
Service discovery N/D Media Team BE/DevOps
Serverless (FaaS) Alto Media Team che adottano servizi gestiti
CQRS Alto (read) Alta Team con competenze su proiezioni
Event sourcing Medio Alta Team data‑intensive/audit
Repository N/D Bassa Qualsiasi team orientato a test
Sharding Alto Alta Team DBA/operativo esperto
Primario/replica Medio (read) Media Team BE con DBA
Static hosting Alto Bassa Team web/app con CDN
Cache‑aside N/D Media Team BE/API
Read‑through cache N/D Media Team BE/API
Outbox N/D Media Team BE/DB/stream
Lambda/Kappa Alto Alta Team data/stream
Circuit breaker N/D Media Team BE/Platform
Bulkhead N/D Media Team BE/Platform
Queue‑based leveling N/D Media Team BE/Platform
Retry/Timeout N/D Bassa Team BE
Saga N/D Alta Team distribuiti esperti
Throttling N/D Media Team API/gateway
Space‑based Alto Alta Team performance/low‑latency
Strangler N/D Media Team modernizzazione
Anti‑corruption layer N/D Media Team DDD/integration
Monolite modulare Medio Media Team piccoli/medi
Onion/Clean Medio Media Team domain‑centric/test
MVVM Medio Media Team UI
DDD N/D Media‑Alta Team prodotto/domain‑centric

Conclusione ✅

I pattern sono strumenti, non religioni. Scegli quelli che massimizzano il valore nel tuo contesto, sorveglia gli effetti nel tempo e mantieni il sistema evolvibile. La vera seniority sta nel sapere cosa NON applicare.

Ultimo aggiornamento il