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.

20 min Architettura del software

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 passaggioCosa faResponsabilità
inputStrumentoId.valuelegge un dato dalla paginaPresentation / UI
inputStudente.valuelegge un altro inputPresentation / UI
strumenti.find(...)cerca uno strumentoaccesso ai dati
if (!strumento)gestisce un caso mancantecontrollo applicativo
strumento.stato === "guasto"verifica una regolaDomain
strumento.stato = "in_prestito"modifica lo statoDomain / Application
prestiti.push(...)registra un nuovo prestitoApplication
localStorage.setItem(...)salva i datiInfrastructure
messaggioElement.textContentmostra un messaggioPresentation / UI
mostraStrumenti(...)aggiorna la listaPresentation / 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_manutenzione senza 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:

AreaDomanda praticaEsempio nel Registro
Presentation / UICome l’utente interagisce con il sistema?form, pulsanti, lista strumenti, messaggi
ApplicationQuale operazione completa devo eseguire?registrare prestito, chiudere prestito, cercare strumenti
DomainQuali regole devono valere sempre?uno strumento guasto non può essere prestato
InfrastructureCome 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:

CambiamentoDove dovrebbe stare l’impatto principale
Cambio il testo del messaggio “Strumento non trovato”Presentation
Cambio la regola sugli strumenti guastiDomain
Cambio il flusso per registrare un prestitoApplication
Cambio localStorage con un file JSONInfrastructure
Cambio il colore dei pulsantiPresentation
Aggiungo un controllo sugli stati ammessiDomain

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.