modulo 03 di 24 / Ore 0-20
Moduli 1-8 / Checkpoint 20 ore
Architettura come organizzazione delle responsabilità
Capire l'architettura software come scelta pratica su dove mettere le responsabilità del programma, partendo dal registro fragile costruito nel modulo precedente.
Architettura come organizzazione delle responsabilità
Nel modulo precedente hai costruito un Registro del laboratorio fragile.
Fragile non significa inutile. Il programma funziona, ma concentra troppe decisioni nello stesso punto: legge input dalla pagina, decide regole, modifica dati, salva in localStorage, mostra messaggi e aggiorna la lista.
Ora possiamo introdurre la parola architettura in modo concreto.
In questo corso architettura significa prima di tutto:
decidere dove stanno le responsabilità importanti del programma.
Non significa creare tante cartelle.
Non significa usare parole difficili.
Non significa copiare un modello famoso e applicarlo ovunque.
Significa rendere chiaro chi fa cosa, perché lo fa e quanto costa cambiarlo.
Obiettivo della lezione
Alla fine devi saper:
- leggere un blocco di codice e riconoscere le responsabilità che contiene;
- distinguere UI, regole, coordinamento, dati e messaggi;
- spiegare perché un programma funzionante può comunque essere fragile;
- capire che l’architettura nasce da problemi pratici, non da decorazione;
- preparare una piccola mappa delle responsabilità del Registro.
Questa lezione non chiede ancora di riscrivere il progetto. Chiede di imparare a guardarlo.
Ripartiamo dal codice fragile
Nel registro fragile una parte del codice può assomigliare a questa:
bottonePresta.addEventListener("click", () => {
const id = inputStrumentoId.value;
const studente = inputStudente.value;
const strumento = strumenti.find((item) => item.id === id);
if (!strumento) {
messaggioElement.textContent = "Strumento non trovato";
return;
}
if (strumento.stato === "guasto") {
messaggioElement.textContent = "Uno strumento guasto non può essere prestato";
return;
}
strumento.stato = "in_prestito";
prestiti.push({
id: `pre-${Date.now()}`,
strumentoId: strumento.id,
studente,
aperto: true,
});
localStorage.setItem("strumenti", JSON.stringify(strumenti));
localStorage.setItem("prestiti", JSON.stringify(prestiti));
messaggioElement.textContent = "Prestito registrato";
mostraStrumenti(strumenti);
});
Il codice è comprensibile. Non è scritto in modo assurdo. Il problema è più sottile: questo blocco fa troppe cose diverse.
Quando un blocco fa troppe cose diverse, ogni modifica diventa rischiosa.
Che cosa fa davvero questo blocco
Proviamo a leggerlo riga per riga.
| Codice o passaggio | Cosa fa | Responsabilità |
|---|---|---|
inputStrumentoId.value | legge un dato dalla pagina | Presentation / UI |
inputStudente.value | legge un altro input | Presentation / UI |
strumenti.find(...) | cerca uno strumento | accesso ai dati |
if (!strumento) | gestisce un caso mancante | controllo applicativo |
strumento.stato === "guasto" | verifica una regola | Domain |
strumento.stato = "in_prestito" | modifica lo stato | Domain / Application |
prestiti.push(...) | registra un nuovo prestito | Application |
localStorage.setItem(...) | salva i dati | Infrastructure |
messaggioElement.textContent | mostra un messaggio | Presentation / UI |
mostraStrumenti(...) | aggiorna la lista | Presentation / UI |
Nessuna di queste responsabilità è sbagliata.
Il problema è che sono tutte nello stesso posto.
Questo rende difficile rispondere a domande semplici:
- dove cambio la regola sul prestito?
- dove cambio il messaggio mostrato all’utente?
- dove cambio il formato dei dati salvati?
- dove scrivo un test per impedire il prestito di uno strumento guasto?
- dove aggiungo lo stato
in_manutenzionesenza rompere tutto?
Se per ogni risposta dobbiamo aprire lo stesso file e toccare lo stesso blocco, il progetto sta diventando fragile.
Responsabilità: una parola pratica
Una responsabilità è un motivo per cui una parte del codice esiste.
Esempi:
- leggere input da un form;
- verificare se uno strumento può essere prestato;
- registrare un prestito;
- salvare una lista di strumenti;
- trasformare un errore tecnico in un messaggio leggibile;
- disegnare una tabella nella pagina.
Una responsabilità non è per forza un file.
All’inizio puoi avere più responsabilità nello stesso file. Succede. Il punto è riuscire a vederle e separarle quando iniziano a disturbarsi.
Una buona domanda è:
se questa parte cambia, perché cambia?
Se cambia per molti motivi diversi, probabilmente contiene troppe responsabilità.
Una prima separazione utile
Nel corso useremo quattro aree:
| Area | Domanda pratica | Esempio nel Registro |
|---|---|---|
| Presentation / UI | Come l’utente interagisce con il sistema? | form, pulsanti, lista strumenti, messaggi |
| Application | Quale operazione completa devo eseguire? | registrare prestito, chiudere prestito, cercare strumenti |
| Domain | Quali regole devono valere sempre? | uno strumento guasto non può essere prestato |
| Infrastructure | Come salvo, leggo o comunico con l’esterno? | memoria, localStorage, JSON, database, API |
Questi nomi servono a orientarci.
Non sono una religione. Non devi trasformare ogni esercizio in una struttura enorme. Devi usarli per capire dove mettere il codice.
Architettura non è sinonimo di cartelle
Un errore comune è pensare:
Creo le cartelle domain, application, infrastructure e presentation.
Quindi ho fatto architettura.
No.
Le cartelle possono aiutare, ma solo se il codice rispetta i confini.
Se dentro presentation metti la regola “uno strumento guasto non può essere prestato”, la cartella non ha risolto il problema.
Se dentro domain usi localStorage, hai portato un dettaglio tecnico dentro le regole.
Se dentro un caso d’uso manipoli direttamente il DOM con document.querySelector, il caso d’uso dipende dalla pagina.
L’architettura non si vede solo dai nomi delle cartelle. Si vede dalle dipendenze e dalle responsabilità.
Il test mentale del cambiamento
Per capire se una responsabilità è nel posto giusto, usa questo test:
Se domani cambio questa cosa, quali parti del progetto devo toccare?
Esempi:
| Cambiamento | Dove dovrebbe stare l’impatto principale |
|---|---|
| Cambio il testo del messaggio “Strumento non trovato” | Presentation |
| Cambio la regola sugli strumenti guasti | Domain |
| Cambio il flusso per registrare un prestito | Application |
Cambio localStorage con un file JSON | Infrastructure |
| Cambio il colore dei pulsanti | Presentation |
| Aggiungo un controllo sugli stati ammessi | Domain |
Se cambi localStorage e devi riscrivere le regole di prestito, c’è confusione.
Se cambi il testo di un messaggio e devi modificare il dominio, c’è confusione.
Se per testare una regola devi aprire il browser e cliccare un pulsante, la regola è troppo legata alla UI.
Prima mappa del Registro
Per il nostro progetto guida possiamo iniziare così:
Presentation / UI
- legge input dai form
- mostra strumenti e prestiti
- mostra messaggi all'utente
- chiama i casi d'uso
Application
- RegistraStrumento
- CercaStrumenti
- RegistraPrestito
- ChiudiPrestito
- ApriSegnalazioneGuasto
Domain
- Strumento
- Prestito
- StatoStrumento
- regole sugli stati
- invarianti del prestito
Infrastructure
- archivio in memoria
- archivio localStorage
- lettura/scrittura JSON
- eventuale database o API
Questa mappa non è ancora codice definitivo. È una bussola.
Quando non sai dove mettere una funzione, torna a questa mappa e chiediti quale responsabilità sta svolgendo.
Esempio: separare una decisione
Nel codice fragile la regola può essere dentro il click:
if (strumento.stato === "guasto") {
messaggioElement.textContent = "Uno strumento guasto non può essere prestato";
return;
}
Una prima separazione può essere questa:
function puoEsserePrestato(strumento) {
return strumento.stato === "disponibile";
}
Non abbiamo ancora costruito tutta l’architettura. Però abbiamo fatto una cosa importante: abbiamo dato un nome a una decisione.
Ora possiamo leggerla:
if (!puoEsserePrestato(strumento)) {
messaggioElement.textContent = "Questo strumento non può essere prestato";
return;
}
La UI mostra ancora il messaggio, ma la decisione inizia a essere separata.
Questo è il tipo di passo che vogliamo imparare: piccolo, leggibile, verificabile.
Errore da evitare
Non trasformare subito tutto in classi, pattern e cartelle.
Se una funzione semplice basta, usa una funzione semplice.
Se una regola può essere espressa con un nome chiaro, parti da lì.
L’obiettivo non è sembrare professionali. L’obiettivo è rendere il progetto meno fragile.
Attività guidata
Apri il tuo main.js fragile e scegli un event listener.
Compila una tabella come questa:
| Riga o blocco | Cosa fa | Responsabilità |
| --- | --- | --- |
| `const id = input.value` | legge input dalla pagina | Presentation |
| `strumenti.find(...)` | cerca lo strumento | accesso ai dati |
| `if (strumento.stato === "guasto")` | applica una regola | Domain |
| `localStorage.setItem(...)` | salva dati | Infrastructure |
Vincolo: non modificare il codice durante questa attività.
Prima osservi. Poi separi.
Micro-sfida
Trova il blocco più carico di responsabilità nel tuo progetto.
Consegna una lista in questo formato:
Blocco analizzato: event listener di "Registra prestito"
Responsabilità individuate:
- legge input dalla UI;
- valida dati mancanti;
- cerca lo strumento;
- applica regole di prestito;
- modifica stato;
- crea un prestito;
- salva dati;
- mostra messaggi;
- aggiorna la lista.
Prima responsabilità da separare:
- regola di prestito.
La micro-sfida è completata solo se indichi anche la prima responsabilità da separare.
Prodotto da ottenere
Alla fine della lezione devi avere:
- una tabella con almeno otto righe analizzate;
- una mappa iniziale delle responsabilità del Registro;
- una frase che spiega perché il codice fragile funziona ma è rischioso;
- una prima responsabilità candidata al refactoring.
Domande di comprensione
Rispondi per iscritto:
- Che cosa significa architettura in questo corso?
- Perché creare cartelle non basta?
- Quale responsabilità non dovrebbe stare nella UI?
- Che cosa dovrebbe cambiare se cambio solo il modo di salvare i dati?
- Perché una regola separata è più facile da testare?
Controllo finale
La lezione è completata quando puoi dire sì a queste domande:
- so spiegare architettura come organizzazione delle responsabilità;
- so distinguere almeno Presentation, Application, Domain e Infrastructure;
- ho analizzato un blocco reale del Registro fragile;
- ho individuato responsabilità mescolate senza riscrivere subito;
- ho capito che le cartelle aiutano solo se i confini sono rispettati;
- so dire perché una regola importante non dovrebbe vivere nel click di un bottone.
Nel prossimo passo parleremo di confini: dove finisce l’utente, dove inizia la UI, dove stanno le regole e dove vivono i dati salvati.