Se sei mai stato in una riunione in cui qualcuno va alla lavagna, disegna un riquadro etichettato “il sistema”, poi un riquadro più piccolo dentro etichettato “API”, poi altri dieci riquadri attorno a quello, e un’ora dopo nessuno riesce a mettersi d’accordo su cosa avrebbero dovuto discutere, hai vissuto il problema centrale dei diagrammi di architettura. I diagrammi non sono il vero problema. Il vero problema è la mancanza di una convenzione condivisa su a che livello di zoom ci si trova.
Stiamo parlando del sistema come una cosa sola in un ecosistema più ampio? Oppure delle deployable units dentro il sistema? O dei moduli dentro una di queste unità? O del codice? Ciascuna è una conversazione utile. Mescolarle nello stesso diagramma è il modo migliore per ritrovarsi con una lavagna piena di ottanta riquadri illeggibili.
Il modello C4 di Simon Brown risolve questo problema. È, a seconda di come lo si guarda, un contributo molto modesto all’ingegneria del software oppure una rivoluzione sottovalutata. È una convenzione. Quattro livelli di zoom standard per parlare di un sistema. Ogni livello ha uno scope chiaro, un insieme chiaro di elementi legittimi, e una domanda chiara a cui risponde. Quando il team accetta di usare la convenzione, le conversazioni diventano più rapide, i diagrammi più chiari, e la lavagna smette di sembrare un Jackson Pollock.
Questa lezione attraversa i quattro livelli, con un esempio pratico per un piccolo prodotto SaaS, e qualche opinione su quando non usare C4.
I quattro livelli
C4 sta per Context, Container, Component, Code. Ciascuno è un livello di zoom. Si parte dal più ampio (Context) e si arriva al più dettagliato (Code), e a ogni livello si mostrano solo gli elementi appropriati a quello zoom.
Livello 1: System Context. Il sistema che stai costruendo, immerso nel mondo più ampio. Mostra il sistema come un riquadro singolo, gli utenti umani (gli “actors”), e i sistemi esterni da cui il tuo sistema dipende o con cui si integra. Il pubblico è ampio: chiunque in azienda, tecnico o no, dovrebbe essere in grado di leggere questo diagramma. La domanda a cui risponde è “cos’è questa cosa, e con cosa parla?”.
Livello 2: Container. Fa zoom dentro il sistema del livello 1. Mostra le unità deployable o eseguibili al suo interno: il frontend web, il servizio API, il database, il message broker, il worker, la cache. “Container” qui è un Brown-ism che precede Docker di anni e si riferisce a “qualsiasi cosa che ospiti codice o dati”, non specificamente a un container Linux, anche se nel 2026 la maggior parte dei container in questo senso gira dentro container reali nel senso Docker. Il pubblico è tecnico: sviluppatori, SRE, architetti. La domanda a cui risponde è “cosa gira dove, e come parlano fra loro queste cose?”.
Livello 3: Component. Fa zoom dentro un container del livello 2. Mostra i moduli principali, i servizi, o i raggruppamenti logici dentro quel container. Per un servizio API, i component potrebbero essere auth, orders, products, billing. Per un frontend, i component potrebbero essere il routing layer, l’auth context, il data fetching layer, il design system. Il pubblico sono gli sviluppatori che lavorano su quel container. La domanda a cui risponde è “come è organizzato internamente questo container?”.
Livello 4: Code. Fa zoom dentro un component del livello 3 e mostra le classi, le interfacce o le funzioni vere e proprie. Diagrammi di classe UML, in pratica, ristretti a un singolo component. Il pubblico è chi sta implementando il component. La domanda a cui risponde è “quali sono i veri oggetti di codice coinvolti?”.
Il trucco di C4 è che disegni i livelli che ti servono e salti quelli che non ti servono. La maggior parte delle conversazioni utili avviene ai livelli 1, 2 e 3. Il livello 4 raramente vale la pena disegnarlo perché, quando sei al livello delle classi, è quasi sempre più rapido leggere il codice stesso che mantenere un diagramma destinato a diventare obsoleto nel momento in cui qualcuno fa refactoring. Brown stesso dice che il livello 4 è opzionale e la maggior parte dei team lo salta.
L’altro aspetto utile: ogni livello ti permette di avere una conversazione a quel livello senza tirare dentro gli altri. Quando il product owner chiede “il sistema si integra con Stripe?” tu indichi il diagramma Context. Non spieghi anche che Stripe viene chiamato dal Spring bean BillingService dentro il package payments del container api. Non gliene importa nulla. Vuole sapere se Stripe è in gioco. Il diagramma Context dice di sì.
Esempio pratico: un piccolo prodotto SaaS
Disegniamo tutti e tre i livelli utili per un ipotetico piccolo prodotto SaaS. Lo chiamiamo ChartSmith, uno strumento online per disegnare grafici. Gli utenti si registrano, fanno login, costruiscono grafici, li salvano e li esportano. I grafici sono memorizzati in un database, esportati tramite un worker in background, fatturati mensilmente tramite Stripe, e le email di conferma partono via SendGrid. C’è un frontend React, un’API Node, un database Postgres, una cache Redis, e un export worker in Python dietro a una job queue.
Quel paragrafo è il sistema in italiano semplice. Ora vediamo come appare ciascun livello C4.
Livello 1: System Context
Il diagramma Context mostra ChartSmith come un riquadro singolo, le persone che lo usano, e i sistemi esterni con cui si integra.
flowchart LR
user(["ChartSmith user"])
admin(["ChartSmith admin"])
chartsmith["<b>ChartSmith</b><br/><i>Web app for building<br/>and exporting charts</i>"]
stripe["<b>Stripe</b><br/><i>Subscription payments</i>"]
sendgrid["<b>SendGrid</b><br/><i>Transactional email</i>"]
s3["<b>AWS S3</b><br/><i>Export storage</i>"]
user --> chartsmith
admin --> chartsmith
chartsmith --> stripe
chartsmith --> sendgrid
chartsmith --> s3
classDef person fill:#e6f4f1,stroke:#0d9488,color:#0c1419
classDef sys fill:#0d9488,stroke:#0d9488,color:#ffffff
classDef ext fill:#fff5e6,stroke:#c89200,color:#5a3e00
class user,admin person
class chartsmith sys
class stripe,sendgrid,s3 ext
Nota cosa non c’è in questo diagramma. Non si parla del database, della cache, di se l’API sia REST o GraphQL, o di se il frontend sia React. Questi dettagli appartengono al livello 2. Il diagramma Context è per la conversazione “ecco il riquadro, ecco il mondo, ecco le linee che li collegano”. È il diagramma che metti nella prima pagina di un design doc, quello che mostri a uno stakeholder non tecnico, e quello che dovrebbe essere leggibile in quindici secondi.
Gli actors sono gli umani. Il riquadro del sistema è ciò che stiamo costruendo. I sistemi esterni sono le dipendenze che non controlliamo. Tre dipendenze esterne per un piccolo SaaS sono normali; un sistema reale tipico ne ha da tre a trenta a seconda di quanto è maturo il prodotto.
Livello 2: Container
Facciamo zoom dentro il riquadro ChartSmith e mostriamo cosa c’è dentro.
flowchart TB
user(["ChartSmith user"])
subgraph chartsmith ["ChartSmith"]
direction TB
spa["<b>Web app</b><br/><i>React, Vite</i>"]
api["<b>API</b><br/><i>Node.js, Express</i>"]
worker["<b>Export worker</b><br/><i>Python</i>"]
db[("<b>Database</b><br/><i>PostgreSQL</i>")]
cache[("<b>Cache</b><br/><i>Redis</i>")]
queue["<b>Job queue</b><br/><i>Redis + BullMQ</i>"]
end
subgraph external ["External services"]
direction TB
stripe["<b>Stripe</b>"]
sendgrid["<b>SendGrid</b>"]
s3["<b>AWS S3</b>"]
end
user --> spa
spa --> api
api --> db
api --> cache
api --> queue
queue --> worker
api --> stripe
api --> sendgrid
worker --> s3
classDef person fill:#e6f4f1,stroke:#0d9488,color:#0c1419
classDef ext fill:#fff5e6,stroke:#c89200,color:#5a3e00
classDef boundary fill:transparent,stroke:#0d9488,stroke-dasharray: 5 5
class user person
class stripe,sendgrid,s3 ext
class chartsmith,external boundary
Questo è il diagramma su cui sviluppatori e SRE litigano. Mostra le vere unità deployable, le scelte tecnologiche per ciascuna, e i protocolli che le collegano. Sei container sono un sistema piccolo. Un SaaS di medie dimensioni avrà da dieci a quindici container in questo diagramma. Un sistema enterprise grande ne avrà così tanti che lo dovrai dividere in più diagrammi Container, uno per sotto-sistema logico, e va benissimo così.
Qualche convenzione che vale la pena notare. I database hanno la forma del cilindro (ContainerDb nella sintassi Mermaid C4). Ogni container ha un’etichetta, una tecnologia, e una descrizione di una riga. Ogni freccia è etichettata con quello che fa e con il protocollo che usa. Quelle etichette sembrano eccessive la prima volta che disegni il diagramma e diventano essenziali la terza volta che lo guardi sei mesi dopo, chiedendoti perché diavolo l’API stia parlando con Redis.
Il diagramma Container è anche dove leggi la deployment topology. Ogni container nel diagramma diventa uno o più processi in produzione. La web app viene servita da una CDN. L’API gira come una flotta di processi Node dietro a un load balancer. Il database è un Postgres gestito. La cache è una Redis gestita. Il worker è una flotta separata di processi Python. Niente di tutto questo è esplicito nel diagramma, ma è tutto implicito da quali riquadri esistono.
Livello 3: Component
Facciamo zoom dentro il container API e mostriamo i moduli principali al suo interno.
flowchart TB
spa["Web app"]
subgraph api ["API container"]
direction TB
router["<b>HTTP router</b><br/><i>Express, auth middleware</i>"]
subgraph features ["Feature components"]
direction LR
auth["<b>Auth</b>"]
charts["<b>Charts</b>"]
exports["<b>Exports</b>"]
billing["<b>Billing</b>"]
notifications["<b>Notifications</b>"]
end
data["<b>Data access</b><br/><i>Postgres + Redis repos</i>"]
end
storage[("DB / Cache / Queue")]
extsvc["Stripe / SendGrid"]
spa --> router
router --> auth
router --> charts
router --> exports
router --> billing
auth --> data
charts --> data
exports --> data
billing --> data
billing --> notifications
auth --> notifications
data --> storage
exports --> storage
notifications --> extsvc
billing --> extsvc
classDef ext fill:#fff5e6,stroke:#c89200,color:#5a3e00
classDef external fill:#f0f4f8,stroke:#5a6a73,color:#0c1419
classDef boundary fill:transparent,stroke:#0d9488,stroke-dasharray: 5 5
class extsvc ext
class spa,storage external
class api,features boundary
Il diagramma Component mostra l’organizzazione interna dell’API. Sette component, ciascuno con una responsabilità chiara. I confini si mappano sulle cartelle del repository, più o meno. I nuovi sviluppatori che entrano nel team API guardano questo diagramma prima di guardare il codice, perché dice loro dove trovare le cose. “Dove viene chiamato Stripe?” Component Billing. “Dov’è l’hashing delle password?” Component Auth. “Dove sono le query SQL?” Component Data access, e solo lì.
La convenzione che paga qui è che i component dovrebbero avere responsabilità singole e dovrebbero comunicare attraverso interfacce chiare. Se disegni il diagramma Component e scopri che ogni component parla con ogni altro component, è il segno che il tuo container ha perso la sua struttura interna, e il diagramma sta facendo un lavoro utile portandola in superficie.
Tipicamente disegneresti un diagramma Component per ciascun container importante che è abbastanza complesso da meritarne uno. La web app potrebbe avere il suo diagramma Component che mostra la struttura del frontend. L’export worker potrebbe averne uno che mostra la pipeline di rendering. Il database non ne ha bisogno. I component dentro un database non esistono davvero nel modo in cui C4 li intende.
Livello 4: Code, e perché di solito lo saltiamo
Il livello 4 farebbe zoom dentro un component (diciamo, il component Auth) e mostrerebbe le classi vere e proprie: UserRepository, SessionService, PasswordHasher, LoginController, e così via, con le relazioni tra loro. Territorio dei diagrammi di classe UML.
Nella pratica quasi nessuno li disegna e quasi nessuno dovrebbe. Il motivo è che i diagrammi di livello 4 sono già obsoleti il giorno in cui li disegni. Le strutture di classi cambiano ogni settimana mentre il team fa refactoring. Mantenere un diagramma di classi UML disegnato a mano è una tassa che nessuno si offre volontariamente di pagare, e i diagrammi di classi generati dall’IDE sono accurati ma raramente utili perché mostrano ogni classe senza alcuna selezione.
L’eccezione è quando stai documentando un pezzo di codice particolarmente sottile: una macchina a stati, un parser, un pattern che a qualcuno sono serviti tre giorni per capirlo e che richiederebbe a un futuro maintainer gli stessi tre giorni per ri-derivarlo senza aiuto. In quei casi, un diagramma di livello 4 si guadagna il pane. Altrimenti, il tuo codice è il diagramma di livello 4. Leggilo.
La guidance di Brown stesso è che i livelli da 1 a 3 coprono circa il 95% delle conversazioni architetturali utili. Ho trovato che è più o meno corretto.
Quando non usare C4
C4 non è lo strumento giusto per ogni situazione. Qualche caso in cui la convenzione è eccessiva o semplicemente sbagliata.
Sistemi molto piccoli. Una singola Lambda con dietro DynamoDB sono due riquadri. Non hai bisogno di un diagramma Context, uno Container e uno Component per due riquadri. Uno schizzo basta.
Prototipi molto iniziali. Quando stai cercando di decidere se un’idea è anche solo fattibile, disegnare diagrammi C4 formali ti rallenta. Uno schizzo a penna su un fazzoletto con riquadri scritti a mano è meglio. C4 inizia a ripagare quando il sistema ha forma e ci sono stakeholder con cui comunicare.
Diagrammi specializzati che hanno le loro convenzioni. Sequence diagram, macchine a stati, diagrammi di topologia di rete, ERD, deployment diagram, dataflow diagram, processi BPMN: hanno tutti convenzioni consolidate e C4 non è un loro sostituto. C4 copre la struttura. Non copre il comportamento, le forme dei dati, o i processi. Usa lo strumento giusto per la domanda.
Team che hanno già un’altra convenzione. Se la tua organizzazione si è standardizzata sui component diagram UML 2, o ArchiMate, o una convenzione fatta in casa che tutti leggono fluentemente, passare a C4 è soprattutto un costo sociale. Il valore di C4 sta in larga parte nel fatto che tutti nel team lo usano allo stesso modo, e qualsiasi convenzione consistente è meglio di nessuna convenzione.
La regola generale: usa C4 quando stai spiegando un sistema non banale a persone che non erano nella stanza quando è stato progettato. Saltalo quando il pubblico e l’artefatto non giustificano la formalità.
Tooling
Qualche strumento che renderizza C4 bene nel 2026.
Mermaid, in-markdown e supportato nella maggior parte dei sistemi di documentazione moderni. I diagrammi in questa lezione sono Mermaid C4. Supporta C4Context, C4Container, C4Component e C4Dynamic. Il renderer è abbastanza buono per la maggior parte degli usi, con qualche occasionale spigolosità nel layout. Il grande vantaggio: i diagrammi vivono nello stesso git repo del codice, sono versionati, e sono revisionati nelle pull request.
Structurizr, lo strumento di Brown stesso. La feature distintiva è che scrivi il modello una volta in un DSL e generi tutti i livelli C4 dalla stessa fonte. Se cambi un container, tutti i diagrammi che lo includono si aggiornano automaticamente. C’è una versione hosted e un’immagine Docker self-hosted. È lo strumento più “C4-native” che esista, e se hai intenzione di investire pesantemente in C4, vale la pena conoscerlo.
diagrams.net (ex draw.io), con gli stencil C4 abilitati. Funziona bene se stai già usando draw.io per tutto il resto e vuoi un editor visuale rapido. Meno rigoroso di Structurizr perché i diagrammi sono fatti a mano e il modello non è imposto da nessuna parte, ma la barriera d’ingresso è più bassa.
PlantUML con C4-PlantUML. Storico, basato su testo, si integra bene con toolchain più datate. Un po’ più verboso di Mermaid ma in alcuni aspetti più ricco di feature. Usato largamente in contesti enterprise che hanno già investito in PlantUML per i sequence diagram e gli ERD.
Per la maggior parte dei team consiglierei di iniziare con Mermaid per i diagrammi che vivono in un repository di documentazione (lo sweet spot di “versionato, revisionabile, basso cerimoniale”) e di passare a Structurizr se e quando i diagrammi iniziano a sembrare abbastanza importanti da rendere conveniente gestirli come modello piuttosto che come un insieme di immagini.
Diagram to create: un poster di overview “C4 zoom levels”. Quattro rettangoli annidati, ciascuno etichettato con il livello (1 Context, 2 Container, 3 Component, 4 Code) e un sottotitolo breve (System in the world, Containers inside the system, Components inside one container, Classes inside one component). A destra di ciascun livello, un’etichetta “audience” di una riga: business stakeholder, sviluppatori e SRE, il team che costruisce il container, l’implementatore del component. Usa un colore consistente per ciascun livello (suggeriti: navy, teal, ambra, ardesia). Titolo in cima: “C4 model: four zoom levels for system diagrams.” Attribuzione della fonte in fondo: “After Simon Brown, c4model.com.”
Cosa dovresti portarti via da questa lezione
C4 è una convenzione con quattro livelli di zoom: Context (sistema nel mondo), Container (deployable units), Component (moduli dentro un container), Code (raramente disegnato). La maggior parte dei diagrammi utili vive ai livelli da 1 a 3. Ogni livello ha un pubblico chiaro e risponde a una domanda chiara. La vittoria pratica più grande dell’usare C4 è che le conversazioni non scivolano tra i livelli di zoom, che è la modalità di fallimento che produce lavagne illeggibili.
Usa Mermaid nel tuo repository di documentazione come la via di minor resistenza. Usa Structurizr se vuoi una singola fonte di verità per tutti i diagrammi. Salta C4 del tutto per sistemi molto piccoli, prototipi molto iniziali, o diagrammi specializzati che hanno le loro convenzioni.
Nella prossima lezione passiamo dal disegnare sistemi al pensare a cosa gira davvero sotto. La lezione 4 guarda alla singola macchina: l’OS, il modello dei processi, i thread, l’async, l’I/O su file, e cosa può fare un solo box prima di averne bisogno di un secondo. La massima “scale up before you scale out” funziona solo se sai cosa lo scale up effettivamente ti compra, ed è lì che andremo.
Riferimenti
- Simon Brown. The C4 model for visualising software architecture, https://c4model.com (recuperato il 2026-05-01).
- Simon Brown. Software Architecture for Developers, vol. 2 (2014, con aggiornamenti continui).
- Mermaid C4 syntax docs, https://mermaid.js.org/syntax/c4.html (recuperato il 2026-05-01).
- Structurizr DSL reference, https://docs.structurizr.com/dsl (recuperato il 2026-05-01).