modulo 06 di 24 / Ore 0-20

Moduli 1-8 / Checkpoint 20 ore

Modellare Prestito: apertura, chiusura e responsabilità

Definire il modello Prestito come ciclo di vita distinto dallo strumento: apertura, chiusura, dati necessari e responsabilità.

25 min Architettura del software

Modellare Prestito: apertura, chiusura e responsabilità

Uno strumento è una cosa. Un prestito è un fatto che accade nel tempo.

Questa distinzione è fondamentale. Lo strumento “Arduino Uno” può esistere nel laboratorio per anni. Un prestito invece inizia in un certo giorno, coinvolge una persona o un gruppo, e prima o poi viene chiuso.

Se confondiamo strumento e prestito, all’inizio il programma sembra più semplice. Poi però diventa difficile rispondere a domande normali:

  • chi ha preso lo strumento?
  • quando è iniziato il prestito?
  • il prestito è ancora aperto?
  • quando è stato restituito?
  • lo strumento è tornato funzionante o con problemi?
  • ci sono stati prestiti precedenti dello stesso strumento?

In questa lezione modelliamo Prestito come concetto separato.

Obiettivo della lezione

Alla fine devi avere un primo modello Prestito con:

  • id;
  • strumentoId;
  • assegnatario;
  • apertoIl;
  • chiusoIl;
  • stato;
  • funzione apriPrestito;
  • funzione chiudiPrestito;
  • regola che impedisce di chiudere due volte lo stesso prestito.

Devi anche saper spiegare la differenza tra:

  • cambiare lo stato corrente dello strumento;
  • registrare il ciclo di vita di un prestito.

Perché non mettere tutto dentro Strumento

Una soluzione fragile potrebbe essere questa:

const strumento = {
  id: "str-001",
  nome: "Arduino Uno",
  categoria: "microcontrollori",
  stato: "in_prestito",
  prestatoA: "Luca Rossi 3A",
  dataPrestito: "2026-05-09",
  restituito: false,
};

Per una prima demo può funzionare. Ma dentro lo stesso oggetto abbiamo mescolato due cose diverse:

  • dati dello strumento;
  • dati del prestito corrente.

Il problema compare appena vogliamo conservare informazioni più precise. Se lo stesso Arduino viene prestato tre volte, dove mettiamo i tre prestiti? Se torna guasto, il guasto appartiene allo strumento o alla chiusura del prestito? Se vogliamo sapere quanto è durato un prestito precedente, abbiamo ancora quei dati?

Lo stato dello strumento dice la condizione corrente:

disponibile
in_prestito
guasto
in_manutenzione

Il prestito racconta una relazione temporanea:

questo strumento è stato assegnato a questa persona da questa data a questa data

Sono collegati, ma non sono la stessa cosa.

Modello minimo di prestito

Un prestito aperto può essere rappresentato così:

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

Campi:

  • id: identifica il prestito;
  • strumentoId: collega il prestito allo strumento;
  • assegnatario: indica a chi è stato dato lo strumento;
  • apertoIl: data di apertura;
  • chiusoIl: data di chiusura, se il prestito è chiuso;
  • stato: indica se il prestito è aperto o chiuso.

La parola assegnatario è volutamente generica. Può essere uno studente, un gruppo, una classe o un docente. Se nel tuo contesto serve essere più precisi, puoi usare studente o ricevente, ma scegli una parola e mantienila in tutto il progetto.

Stato del prestito

Per ora usiamo solo due stati:

aperto
chiuso

Possiamo definirli in src/domain/prestito.js:

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

Non serve aggiungere subito stati come:

scaduto
annullato
contestato
in_verifica

Sono possibili estensioni future. Per il percorso attuale ci basta modellare bene apertura e chiusura.

Aprire un prestito

File:

src/domain/prestito.js

Prima funzione:

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

export function apriPrestito({ id, strumentoId, assegnatario, apertoIl }) {
  if (!id || id.trim() === "") {
    throw new Error("ID_PRESTITO_OBBLIGATORIO");
  }

  if (!strumentoId || strumentoId.trim() === "") {
    throw new Error("STRUMENTO_PRESTITO_OBBLIGATORIO");
  }

  if (!assegnatario || assegnatario.trim() === "") {
    throw new Error("ASSEGNATARIO_PRESTITO_OBBLIGATORIO");
  }

  if (!apertoIl) {
    throw new Error("DATA_APERTURA_PRESTITO_OBBLIGATORIA");
  }

  return {
    id: id.trim(),
    strumentoId: strumentoId.trim(),
    assegnatario: assegnatario.trim(),
    apertoIl,
    chiusoIl: null,
    stato: StatoPrestito.APERTO,
  };
}

Questa funzione controlla che il prestito abbia i dati minimi per esistere.

Nota importante: qui non controlliamo ancora se lo strumento è disponibile. Perché? Perché questa funzione riceve solo i dati del prestito. La disponibilità riguarda anche lo strumento. Sarà responsabilità del caso d’uso RegistraPrestito coordinare il controllo:

trova strumento -> verifica se è prestabile -> apri prestito -> cambia stato strumento -> salva

Questa distinzione evita di trasformare Prestito in un oggetto che sa tutto del sistema.

Chiudere un prestito

Un prestito aperto può essere chiuso. Un prestito chiuso non può essere chiuso di nuovo.

Funzione:

export function chiudiPrestito(prestito, chiusoIl) {
  if (prestito.stato === StatoPrestito.CHIUSO) {
    throw new Error("PRESTITO_GIA_CHIUSO");
  }

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

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

Questa regola appartiene al dominio:

Un prestito chiuso non può essere chiuso di nuovo.

Non dipende dalla UI. Non dipende da localStorage. Non dipende dal pulsante che l’utente clicca. È una regola del ciclo di vita del prestito.

Perché restituiamo un nuovo oggetto

Nell’esempio chiudiPrestito restituisce un nuovo oggetto:

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

Invece di modificare direttamente:

prestito.chiusoIl = chiusoIl;
prestito.stato = StatoPrestito.CHIUSO;

Per questo corso non serve trasformare l’immutabilità in una regola assoluta. Però restituire un nuovo oggetto ha un vantaggio didattico: rende più chiaro che la funzione prende un prestito aperto e produce una versione chiusa.

È più facile da testare e riduce gli effetti nascosti. Se in un progetto futuro preferirai mutare l’oggetto, dovrai farlo consapevolmente.

Esempio di apertura valida

const prestito = apriPrestito({
  id: "pre-001",
  strumentoId: "str-001",
  assegnatario: "Luca Rossi 3A",
  apertoIl: "2026-05-09",
});

Risultato atteso:

{
  id: "pre-001",
  strumentoId: "str-001",
  assegnatario: "Luca Rossi 3A",
  apertoIl: "2026-05-09",
  chiusoIl: null,
  stato: "aperto",
}

Esempi non validi

Senza assegnatario:

apriPrestito({
  id: "pre-002",
  strumentoId: "str-001",
  assegnatario: "",
  apertoIl: "2026-05-09",
});

Errore atteso:

ASSEGNATARIO_PRESTITO_OBBLIGATORIO

Senza strumento:

apriPrestito({
  id: "pre-003",
  strumentoId: "",
  assegnatario: "Sara Verdi 4B",
  apertoIl: "2026-05-09",
});

Errore atteso:

STRUMENTO_PRESTITO_OBBLIGATORIO

Senza data di apertura:

apriPrestito({
  id: "pre-004",
  strumentoId: "str-002",
  assegnatario: "Gruppo robotica",
  apertoIl: null,
});

Errore atteso:

DATA_APERTURA_PRESTITO_OBBLIGATORIA

Esempio di chiusura

const prestitoAperto = apriPrestito({
  id: "pre-001",
  strumentoId: "str-001",
  assegnatario: "Luca Rossi 3A",
  apertoIl: "2026-05-09",
});

const prestitoChiuso = chiudiPrestito(prestitoAperto, "2026-05-16");

Risultato atteso:

{
  id: "pre-001",
  strumentoId: "str-001",
  assegnatario: "Luca Rossi 3A",
  apertoIl: "2026-05-09",
  chiusoIl: "2026-05-16",
  stato: "chiuso",
}

Se proviamo a chiuderlo di nuovo:

chiudiPrestito(prestitoChiuso, "2026-05-17");

Errore atteso:

PRESTITO_GIA_CHIUSO

Questa è una delle regole più semplici e importanti del dominio.

Responsabilità del modello Prestito

Il modello Prestito deve sapere:

  • quali dati minimi servono per aprire un prestito;
  • quando un prestito è aperto;
  • quando un prestito è chiuso;
  • quando un prestito può essere chiuso;
  • come produrre una versione chiusa del prestito.

Non deve sapere:

  • come leggere i dati dal form;
  • come salvare in localStorage;
  • come mostrare il messaggio di conferma;
  • come cercare lo strumento in archivio;
  • come cambiare lo stato dello strumento;
  • come generare tutta la pagina HTML.

Queste responsabilità stanno in altri livelli.

Prestito e caso d’uso

Attenzione: aprire un prestito nel dominio non è lo stesso che registrare un prestito nel sistema.

La funzione apriPrestito costruisce un prestito valido. Il caso d’uso RegistraPrestito dovrà coordinare un’operazione più ampia:

1. ricevere il comando dalla UI;
2. trovare lo strumento nell'archivio;
3. verificare che lo strumento sia prestabile;
4. creare il prestito;
5. cambiare lo stato dello strumento in in_prestito;
6. salvare prestito e strumento aggiornato;
7. restituire un risultato alla UI.

Questa sequenza non deve stare tutta nel dominio Prestito. Il dominio offre regole e piccoli comportamenti. Il caso d’uso mette insieme i pezzi.

Questa distinzione tornerà spesso nel corso.

Scheda del modello

Aggiorna docs/modello-dominio.md o il README:

# Modello Prestito

## Scopo

Rappresenta l'assegnazione temporanea di uno strumento a una persona o a un gruppo.

## Campi

- id: identificativo stabile del prestito.
- strumentoId: id dello strumento prestato.
- assegnatario: persona o gruppo che riceve lo strumento.
- apertoIl: data di apertura del prestito.
- chiusoIl: data di chiusura, se il prestito è chiuso.
- stato: aperto o chiuso.

## Regole minime

- id obbligatorio.
- strumentoId obbligatorio.
- assegnatario obbligatorio.
- data di apertura obbligatoria.
- un prestito aperto può essere chiuso.
- un prestito chiuso non può essere chiuso di nuovo.

## Domande aperte

- Serve registrare chi ha autorizzato il prestito?
- Serve una data prevista di restituzione?
- Serve distinguere studente, gruppo e docente?
- Serve registrare problemi alla chiusura del prestito?

Le domande aperte ci preparano alle estensioni, ma non devono bloccare il modello minimo.

Attività pratica

Scrivi tre prestiti validi:

  • uno per un Arduino;
  • uno per un multimetro;
  • uno per un kit sensori.

Poi scrivi quattro casi non validi:

  • senza id;
  • senza strumentoId;
  • senza assegnatario;
  • senza data di apertura.

Infine prendi un prestito valido e chiudilo. Poi prova a chiuderlo una seconda volta e annota l’errore atteso.

Per ogni caso scrivi:

  • input;
  • risultato atteso;
  • regola che stai verificando.

Cosa produrre

Alla fine della lezione devi avere:

  • file src/domain/prestito.js;
  • costante StatoPrestito;
  • funzione apriPrestito;
  • funzione chiudiPrestito;
  • almeno tre esempi validi;
  • almeno quattro esempi non validi;
  • una nota che distingue Strumento e Prestito;
  • scheda del modello Prestito nel README o in docs/modello-dominio.md.

Controllo finale

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

  • so spiegare la differenza tra strumento e prestito;
  • un prestito ha un proprio id;
  • un prestito si collega allo strumento tramite strumentoId;
  • un prestito aperto può essere chiuso;
  • un prestito chiuso non può essere richiuso;
  • non ho messo lettura della UI dentro Prestito;
  • non ho messo localStorage dentro Prestito;
  • so spiegare che RegistraPrestito sarà un caso d’uso più ampio della sola funzione apriPrestito.

Nella prossima lezione diamo un nome alle regole che devono valere sempre: invarianti.