modulo 08 di 24 / Ore 0-20

Moduli 1-8 / Checkpoint 20 ore

Spostare i casi d'uso fuori dagli event listener

Ridurre gli event listener a codice di bordo: raccolgono input, chiamano casi d'uso e mostrano risultati.

30 minArchitettura del software

Spostare i casi d’uso fuori dagli event listener

Nel programma fragile gli event listener fanno tutto.

Esempio:

click sul bottone
  legge input
  cerca dati
  applica regole
  crea prestito
  salva
  aggiorna UI

Questo rende il codice difficile da testare e modificare.

L’obiettivo e far diventare l’event listener un bordo sottile.

Obiettivo

Alla fine devi avere:

  • un event listener che legge input;
  • un caso d’uso chiamato dall’event listener;
  • logica applicativa fuori dalla UI;
  • gestione degli errori piu chiara.

Punto di partenza

Esempio fragile:

formPrestito.addEventListener("submit", (event) => {
  event.preventDefault();

  const strumentoId = inputStrumentoId.value;
  const assegnatario = inputAssegnatario.value;
  const strumento = strumenti.find((item) => item.id === strumentoId);

  if (!strumento) {
    messaggio.textContent = "Strumento non trovato";
    return;
  }

  if (strumento.stato !== "disponibile") {
    messaggio.textContent = "Strumento non disponibile";
    return;
  }

  const prestito = {
    id: crypto.randomUUID(),
    strumentoId,
    assegnatario,
    stato: "aperto",
  };

  prestiti.push(prestito);
  strumento.stato = "in_prestito";
  messaggio.textContent = "Prestito registrato";
  renderizzaStrumenti();
});

Qui l’event listener contiene un caso d’uso intero.

Estrarre il comando

La UI deve preparare un comando:

function leggiComandoPrestito() {
  return {
    prestitoId: crypto.randomUUID(),
    strumentoId: inputStrumentoId.value,
    assegnatario: inputAssegnatario.value,
    apertoIl: new Date().toISOString().slice(0, 10),
  };
}

Questo e codice di presentation.

Legge input e prepara dati.

Chiamare il caso d’uso

L’event listener diventa:

formPrestito.addEventListener("submit", (event) => {
  event.preventDefault();

  try {
    const comando = leggiComandoPrestito();
    registraPrestito(comando);
    mostraMessaggio("Prestito registrato.");
    aggiornaSchermata();
  } catch (errore) {
    mostraMessaggio(messaggioPerErrore(errore));
  }
});

Ora e piu leggibile.

La UI:

  • legge input;
  • chiama il caso d’uso;
  • mostra risultato;
  • aggiorna la schermata.

Il caso d’uso coordina l’operazione.

Dove creare il caso d’uso

Nel punto di avvio:

const registraPrestito = creaRegistraPrestito({
  archivioStrumenti,
  archivioPrestiti,
});

avviaUIRegistro({
  registraPrestito,
});

La UI riceve il caso d’uso gia pronto.

Non deve costruire archivi da sola se possiamo evitarlo.

Prima e dopo

Prima:

event listener = operazione completa

Dopo:

event listener = bordo
caso d'uso = operazione completa
dominio = regole
archivio = dati

Questa e la forma che vogliamo al checkpoint 20 ore.

Errori

L’event listener non deve contenere una lunga catena di regole:

if (strumento.stato === "guasto") ...
if (strumento.stato === "in_manutenzione") ...

Deve limitarsi a:

try {
  registraPrestito(comando);
} catch (errore) {
  mostraMessaggio(messaggioPerErrore(errore));
}

Questo rende piu facile cambiare le regole senza toccare la UI.

Esercizio

Scegli un event listener del tuo progetto.

Compila:

## Event listener da alleggerire

Evento:

Responsabilita presenti ora:
- ...

Caso d'uso da chiamare:

Comando da costruire:

Codice da lasciare nella UI:
- ...

Codice da spostare fuori:
- ...

Poi modifica un solo listener.

Verifica che il comportamento sia identico.

Cosa produrre

Alla fine della lezione devi avere:

  • almeno un event listener semplificato;
  • una funzione che costruisce il comando;
  • un caso d’uso chiamato dalla UI;
  • gestione errori con try/catch o forma equivalente;
  • una nota sul refactoring fatto.

Spiegazione guidata per studiare

Questa lezione va studiata su due piani. Il primo è il comportamento visibile del Registro del laboratorio: che cosa vede o fa l’utente quando registra uno strumento, cerca un dispositivo, apre un prestito o segnala un guasto. Il secondo è il confine architetturale: dove deve stare il codice che rende possibile quel comportamento.

Per spostare i casi d’uso fuori dagli event listener, il punto centrale è questo: Un caso d’uso coordina un’operazione completa: riceve un comando, legge ciò che serve, applica regole, salva il risultato e restituisce un esito.

Il rischio tipico è far diventare il caso d’uso una seconda UI o un secondo database. Deve coordinare, non mostrare HTML e non conoscere dettagli tecnici di salvataggio. Per evitarlo, non partire dal file che hai già aperto nell’editor. Parti dalla domanda: “quale responsabilità sto modificando?”. Se la risposta riguarda una regola del laboratorio, guarda il dominio. Se riguarda una sequenza di operazioni, guarda i casi d’uso. Se riguarda salvataggio, file, localStorage, API o database, guarda l’infrastruttura. Se riguarda input, pulsanti, form e messaggi, guarda la Presentation.

Il tempo è sufficiente per alternare spiegazione, modifica guidata e revisione. Alla fine della lezione lo studente non deve ricordare una definizione a memoria: deve saper indicare un punto del progetto, spiegare perché quel codice sta lì e mostrare una verifica del comportamento.

Passo per passo nel progetto

  1. Rileggi il titolo della lezione e riscrivilo come domanda pratica. Per esempio: “dove deve stare questa regola?”, “chi deve salvare questo dato?”, “come verifico questo errore?”.
  2. Apri solo i file necessari. Se devi toccare più di tre file, fermati e scrivi prima una mini-mappa.
  3. Definisci input e output del caso d’uso.
  4. Leggi i dati attraverso un archivio o una dipendenza esplicita.
  5. Chiama le regole del dominio.
  6. Salva il risultato senza sapere come funziona la persistenza concreta.
  7. Aggiorna una nota breve nel README o negli appunti se hai cambiato un confine del progetto.

Esempio da leggere lentamente

Questo esempio serve a ragionare, non a incollare codice senza capire:

function registraPrestito(comando, archivioStrumenti, archivioPrestiti) {
  const strumento = archivioStrumenti.trovaPerId(comando.strumentoId);
  verificaStrumentoPrestabile(strumento);
  const prestito = apriPrestito(strumento.id, comando.nomePersona);
  archivioPrestiti.salva(prestito);
  archivioStrumenti.salva({ ...strumento, stato: "in_prestito" });
  return prestito;
}

Leggilo chiedendoti che cosa cambierebbe se domani sostituissimo la pagina web con una CLI, oppure localStorage con un’API. Se l’esempio continua ad avere senso senza riscrivere tutto, il confine è probabilmente buono. Se invece un dettaglio tecnico si propaga in molti file, il progetto sta tornando fragile.

Prova di comprensione

Prima di chiudere la lezione, rispondi per iscritto a queste domande:

  • Qual è la responsabilità principale trattata in questa lezione?
  • In quale livello del progetto dovrebbe stare?
  • Quale errore faresti se cercassi la soluzione più veloce?
  • Quale piccolo test, controllo manuale o esempio dimostra che hai capito?
  • Che cosa deve restare facile da cambiare dopo questa lezione?

Una risposta accettabile non deve essere lunga. Deve però usare parole concrete del Registro: strumento, prestito, stato, archivio, caso d’uso, messaggio, test.

Controllo finale

La lezione e completata quando puoi rispondere si:

  • l’event listener non contiene piu tutta l’operazione;
  • la UI costruisce un comando;
  • il caso d’uso coordina l’operazione;
  • le regole stanno nel dominio;
  • gli errori vengono trasformati in messaggi utente;
  • posso testare il caso d’uso senza click.

Nella prossima lezione creiamo un archivio in memoria.