modulo 03 di 24 / Ore 0-20

Moduli 1-8 / Checkpoint 20 ore

Esercizio: evidenziare i confini nel programma fragile

Esercizio guidato per evidenziare nel programma fragile le parti che appartengono a UI, casi d'uso, dominio e infrastruttura.

20 minArchitettura del software

Esercizio: evidenziare i confini nel programma fragile

In questa lezione non aggiungeremo funzionalità.

Faremo un esercizio di lettura: evidenziare i confini nel programma fragile.

L’obiettivo è allenare lo sguardo. Prima di separare il codice in file, devi saper riconoscere quali parti appartengono a responsabilità diverse.

Questa è una lezione di passaggio: chiude il modulo sull’architettura come responsabilità, confini e decisioni costose.

Obiettivo della lezione

Alla fine devi avere:

  • tre blocchi del programma fragile annotati con etichette;
  • una lista delle responsabilità trovate;
  • almeno una riga ambigua discussa;
  • un punto candidato per il primo refactoring;
  • una nota finale che spiega cosa hai capito del programma fragile.

Non devi ancora pulire tutto. Devi vedere con precisione cosa è mescolato.

Prepara una legenda

Usa quattro etichette:

[UI]
[APPLICATION]
[DOMAIN]
[INFRASTRUCTURE]

Significato:

Etichetta Domanda pratica Esempi
[UI] riguarda l’interazione con l’utente? input, bottoni, render, messaggi
[APPLICATION] coordina un’operazione completa? registrare prestito, cercare strumento, chiudere prestito
[DOMAIN] decide una regola del laboratorio? guasto non prestabile, stato valido, prestito già chiuso
[INFRASTRUCTURE] parla con strumenti tecnici esterni? localStorage, JSON, file, API, database

Per l’esercizio puoi scrivere queste etichette come commenti temporanei nel codice o copiarle in un file di note.

Non devono restare per sempre nel progetto. Sono evidenziatori didattici.

Esempio non annotato

Partiamo da un event listener fragile:

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 blocco funziona, ma contiene livelli diversi.

Esempio annotato

Ora evidenziamolo:

// [UI] evento generato da un click nella pagina
bottonePresta.addEventListener("click", () => {
  // [UI] lettura dei campi del form
  const id = inputStrumentoId.value;
  const studente = inputStudente.value;

  // [APPLICATION] cerca il dato necessario per completare l'operazione
  const strumento = strumenti.find((item) => item.id === id);

  // [APPLICATION] gestisce il caso in cui l'operazione non può proseguire
  if (!strumento) {
    // [UI] messaggio mostrato all'utente
    messaggioElement.textContent = "Strumento non trovato";
    return;
  }

  // [DOMAIN] regola: uno strumento guasto non può essere prestato
  if (strumento.stato === "guasto") {
    // [UI] messaggio collegato all'esito della regola
    messaggioElement.textContent = "Uno strumento guasto non può essere prestato";
    return;
  }

  // [DOMAIN] cambio di stato dello strumento
  strumento.stato = "in_prestito";

  // [APPLICATION] creazione del prestito come parte dell'operazione
  prestiti.push({
    id: `pre-${Date.now()}`,
    strumentoId: strumento.id,
    studente,
    aperto: true,
  });

  // [INFRASTRUCTURE] salvataggio nel browser
  localStorage.setItem("strumenti", JSON.stringify(strumenti));
  localStorage.setItem("prestiti", JSON.stringify(prestiti));

  // [UI] comunicazione e aggiornamento visivo
  messaggioElement.textContent = "Prestito registrato";
  mostraStrumenti(strumenti);
});

Questa annotazione non è perfetta.

Va bene così.

Serve a vedere il problema: dentro un solo click ci sono UI, coordinamento, regole e persistenza.

Righe ambigue

Alcune righe non sono facili da etichettare.

Esempio:

const strumento = strumenti.find((item) => item.id === id);

Potresti dire:

  • è dominio, perché parla di strumenti;
  • è applicazione, perché serve al caso d’uso;
  • è infrastruttura, perché sta cercando dati in un archivio in memoria.

In questa fase la cosa importante non è scegliere l’etichetta perfetta.

La cosa importante è discutere il motivo.

Una scelta ragionevole è [APPLICATION], perché la riga serve a coordinare l’operazione RegistraPrestito: prima devo trovare lo strumento, poi posso applicare le regole.

Più avanti, quando avremo un archivio, questa responsabilità potrà spostarsi dietro un metodo come:

archivioStrumenti.trovaPerId(id);

Sequenza dei livelli

Dopo l’annotazione potresti vedere una sequenza come questa:

[UI]
[APPLICATION]
[UI]
[DOMAIN]
[UI]
[DOMAIN]
[APPLICATION]
[INFRASTRUCTURE]
[UI]

Questa sequenza è il motivo per cui il blocco è fragile.

Il problema non è che esista la UI.

Il problema non è che esista localStorage.

Il problema è che tutti i livelli sono agganciati nello stesso punto.

Cosa annotare nel file di note

Nel file note-architettura.md aggiungi una sezione così:

## Confini individuati nel codice fragile

### Event listener Registra prestito

- UI: legge input e mostra messaggi.
- Application: cerca lo strumento e coordina la creazione del prestito.
- Domain: controlla lo stato dello strumento e decide se il prestito è permesso.
- Infrastructure: salva strumenti e prestiti in localStorage.

Problema:
tutti questi livelli sono nello stesso blocco.

Possibile evoluzione:
estrarre prima una regola di dominio, poi un caso d'uso, poi un archivio.

Questa nota prepara il lavoro dei moduli successivi.

Non è documentazione decorativa: è una lista di interventi futuri.

Attività guidata

Scegli tre blocchi del tuo programma fragile:

  1. registrazione prestito;
  2. cambio stato;
  3. ricerca strumenti.

Per ciascuno:

  • aggiungi etichette temporanee [UI], [APPLICATION], [DOMAIN], [INFRASTRUCTURE];
  • scrivi quali responsabilità compaiono;
  • segnala almeno una riga ambigua;
  • indica la responsabilità che separeresti per prima.

Non modificare ancora il comportamento.

Questa è lettura architetturale, non refactoring.

Micro-sfida

Trova un blocco che contiene almeno tre etichette diverse.

Poi rispondi:

Blocco:

Etichette presenti:

Responsabilità più urgente da separare:

Perché proprio quella:

Comportamento da proteggere prima di modificare:

Esempio:

Responsabilità più urgente da separare:
la regola "uno strumento guasto non può essere prestato".

Perché proprio quella:
deve valere anche fuori dalla UI ed è facile da testare.

Comportamento da proteggere:
quando provo a prestare uno strumento guasto, il prestito non viene creato.

Prodotto da ottenere

Alla fine della lezione devi avere:

  • tre blocchi annotati;
  • una tabella o lista delle responsabilità trovate;
  • almeno una riga ambigua spiegata;
  • una nota Confini individuati nel codice fragile;
  • un primo refactoring candidato;
  • una frase che spiega perché non conviene riscrivere tutto subito.

Esempio:

Non riscrivo tutto subito perché prima devo proteggere i comportamenti già
funzionanti e separare una responsabilità alla volta.

Domande di comprensione

Rispondi per iscritto:

  • Perché annotare i livelli prima di spostare codice?
  • Quale blocco contiene più responsabilità nel tuo progetto?
  • Quale riga ti è sembrata più ambigua?
  • Quale responsabilità separeresti per prima?
  • Quale comportamento devi verificare prima di fare refactoring?

Controllo finale

La lezione è completata quando puoi dire sì a queste domande:

  • ho etichettato almeno tre blocchi di codice fragile;
  • so riconoscere UI, Application, Domain e Infrastructure;
  • ho capito che alcune righe possono essere ambigue;
  • ho scritto una nota sui confini individuati;
  • ho trovato un punto candidato per il primo refactoring;
  • so spiegare perché non serve riscrivere tutto subito.

Hai chiuso il Modulo 3. Ora hai una mappa del problema: sai che cosa funziona, cosa è fragile, quali confini sono confusi e quali decisioni rischiano di diventare costose.

Nel prossimo modulo entreremo in responsabilità, coesione e accoppiamento in modo più operativo.