modulo 06 di 24 / Ore 0-20

Moduli 1-8 / Checkpoint 20 ore

Regola: un prestito chiuso non si chiude due volte

Proteggere il ciclo di vita del Prestito impedendo chiusure duplicate e stati incoerenti.

25 min Architettura del software

Regola: un prestito chiuso non si chiude due volte

Un prestito ha un ciclo di vita semplice:

aperto -> chiuso

Una volta chiuso, non deve essere chiuso di nuovo.

Sembra una regola piccola, ma protegge il sistema da dati incoerenti:

  • date di chiusura sovrascritte;
  • note perse;
  • stato dello strumento cambiato due volte;
  • storico poco affidabile.

Obiettivo

Alla fine devi avere:

  • una funzione chiudiPrestito che rifiuta prestiti gia chiusi;
  • uno scenario di test;
  • una distinzione tra modifica valida e modifica pericolosa;
  • una regola chiara sul ciclo di vita del prestito.

Il caso semplice

Prestito aperto:

const prestito = {
  id: "p-001",
  strumentoId: "s-001",
  assegnatario: "Luca 3A",
  apertoIl: "2026-05-09",
  chiusoIl: null,
  stato: "aperto",
};

Chiusura:

const prestitoChiuso = chiudiPrestito(prestito, "2026-05-10");

Risultato:

{
  id: "p-001",
  strumentoId: "s-001",
  assegnatario: "Luca 3A",
  apertoIl: "2026-05-09",
  chiusoIl: "2026-05-10",
  stato: "chiuso",
}

Questa e una transizione valida.

Il caso da bloccare

Prestito gia chiuso:

const prestito = {
  id: "p-001",
  strumentoId: "s-001",
  assegnatario: "Luca 3A",
  apertoIl: "2026-05-09",
  chiusoIl: "2026-05-10",
  stato: "chiuso",
};

Tentativo:

chiudiPrestito(prestito, "2026-05-11");

Questo deve fallire.

Altrimenti la data originale viene sovrascritta e lo storico perde significato.

Implementazione

File:

src/domain/prestito.js

Codice:

export const StatoPrestito = {
  APERTO: "aperto",
  CHIUSO: "chiuso",
};

export function isPrestitoChiuso(prestito) {
  return prestito.stato === StatoPrestito.CHIUSO;
}

export function chiudiPrestito(prestito, chiusoIl) {
  if (isPrestitoChiuso(prestito)) {
    throw new Error("PRESTITO_GIA_CHIUSO");
  }

  if (!chiusoIl) {
    throw new Error("DATA_CHIUSURA_PRESTITO_OBBLIGATORIA");
  }

  return {
    ...prestito,
    chiusoIl,
    stato: StatoPrestito.CHIUSO,
  };
}

Questa funzione non aggiorna lo strumento.

Il cambio dello strumento, per esempio da in_prestito a disponibile, verra coordinato dal caso d’uso ChiudiPrestito.

Perche restituire un nuovo oggetto

Nel codice sopra non modifichiamo direttamente il prestito originale.

Facciamo:

return {
  ...prestito,
  chiusoIl,
  stato: StatoPrestito.CHIUSO,
};

Questo rende piu chiaro che la funzione produce una nuova versione del prestito.

Non e obbligatorio in ogni progetto, ma e utile nel corso per ridurre effetti collaterali nascosti.

Scenario di test

## Regola

Un prestito chiuso non puo essere chiuso di nuovo.

## Scenario

Dato un prestito con stato chiuso,
quando provo a chiuderlo una seconda volta,
allora ottengo errore PRESTITO_GIA_CHIUSO.

Test possibile:

test("un prestito chiuso non puo essere chiuso di nuovo", () => {
  const prestito = {
    id: "p-001",
    strumentoId: "s-001",
    assegnatario: "Luca 3A",
    apertoIl: "2026-05-09",
    chiusoIl: "2026-05-10",
    stato: "chiuso",
  };

  expect(() => chiudiPrestito(prestito, "2026-05-11")).toThrow(
    "PRESTITO_GIA_CHIUSO"
  );
});

Chiusura e stato dello strumento

Quando chiudiamo un prestito, lo strumento potrebbe:

  • tornare disponibile;
  • diventare guasto;
  • andare in manutenzione.

Questa decisione non deve essere nascosta dentro chiudiPrestito.

Perche chiudiPrestito conosce il prestito, non necessariamente lo strumento.

Il caso d’uso dovra coordinare:

chiudi prestito
decidi stato finale strumento
salva prestito aggiornato
salva strumento aggiornato

Questa separazione evita che il modello Prestito diventi un contenitore di tutte le regole del sistema.

Esercizio

Scrivi tre scenari:

  1. chiusura valida di un prestito aperto;
  2. tentativo di chiusura di un prestito gia chiuso;
  3. tentativo di chiusura senza data.

Per ogni scenario indica:

  • dati iniziali;
  • azione;
  • risultato atteso;
  • errore atteso, se presente.

Cosa produrre

Alla fine della lezione devi avere:

  • funzione chiudiPrestito;
  • funzione isPrestitoChiuso o equivalente;
  • errore PRESTITO_GIA_CHIUSO;
  • almeno tre scenari;
  • nota su chi decide lo stato finale dello strumento.

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 regola: un prestito chiuso non si chiude due volte, il punto centrale è questo: Il dominio contiene il significato del problema: strumenti, prestiti, stati ammessi e regole che devono valere anche se cambia la UI o il modo in cui salviamo i dati.

Il rischio tipico è confondere un controllo di schermata con una regola del progetto. Se una regola protegge il senso del Registro, deve vivere nel dominio o vicino al caso d’uso. 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. Scrivi la regola in linguaggio naturale.
  4. Trasformala in una funzione o in un metodo con un nome leggibile.
  5. Verifica almeno un caso valido e un caso non valido.
  6. Controlla che la regola non sia duplicata nella UI.
  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 verificaStrumentoPrestabile(strumento) {
  if (strumento.stato === "guasto") {
    throw new Error("STRUMENTO_GUASTO_NON_PRESTABILE");
  }
  if (strumento.stato === "in_prestito") {
    throw new Error("STRUMENTO_GIA_IN_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:

  • un prestito aperto puo essere chiuso;
  • un prestito chiuso non puo essere chiuso di nuovo;
  • la data di chiusura e obbligatoria;
  • la funzione non modifica direttamente la UI;
  • il caso d’uso coordinera prestito e strumento.

Nella prossima lezione rendiamo gli errori di dominio piu comprensibili e gestibili.