modulo 17 di 24 / Ore 41-60
Moduli 17-24 / Checkpoint 60 ore
Dimostrare che dominio e casi d'uso non cambiano
Come dimostrare durante la presentazione che cambiando l'archivio il dominio non cambia: eseguire gli stessi test con localStorage e con archivio in memoria.
Dimostrare che dominio e casi d’uso non cambiano
Questa lezione appartiene al Modulo 17 — Variante database o API.
Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. In questa lezione prepari una dimostrazione concreta che il dominio e i casi d’uso sono indipendenti dall’archivio — cambiando solo l’adapter, il comportamento del sistema resta uguale.
Obiettivo della lezione
Eseguire la stessa suite di test con due archivi diversi — localStorage e archivio in memoria — e dimostrare che i risultati sono identici. I casi d’uso non cambiano, non vengono toccati, non sanno quale archivio stanno usando.
Alla fine devi avere: un file di test che usa lo stesso set di test con due adapter diversi, con output identico.
Perché questa dimostrazione è importante
L’architettura a livelli del Registro promette che la persistenza è un dettaglio sostituibile. Questa è la prova concreta di quella promessa. Se cambi l’adapter e i casi d’uso smettono di funzionare, significa che l’architettura ha una falla — i casi d’uso dipendono da qualcosa di specifico dell’adapter.
Durante la presentazione finale, questa dimostrazione vale più di qualsiasi spiegazione verbale: mostra in diretta che il confine tra application e infrastructure funziona davvero.
Il principio di sostituzione
Il principio che si dimostra è questo: se due componenti rispettano lo stesso contratto, sono interscambiabili. Il contratto degli adapter del Registro è:
caricaTutti() // → Strumento[]
salva(strumenti) // → void
trovaPerId(id) // → Strumento | null
Qualsiasi modulo che implementa queste tre funzioni può essere usato al posto di un altro. I casi d’uso non distinguono tra i due.
Procedura guidata
- Crea un archivio in memoria che rispetta il contratto.
- Scrivi cinque test che usano i casi d’uso reali del Registro.
- Esegui i test con l’archivio in memoria — tutti devono passare.
- Esegui gli stessi test con l’adapter localStorage (se possibile in Node.js, o con un mock).
- Mostra che l’output è identico.
Attività in classe
Completa questa scheda dopo la dimostrazione:
## Dimostrazione sostituzione archivio
Test eseguiti: (elenco)
Con archivio in memoria: passati N/N
Con archivio localStorage: passati N/N
Modifiche ai casi d'uso necessarie: sì / no
Cosa prova questa dimostrazione:
Spiegazione guidata per studiare
La differenza tra un’architettura che “funziona” e una che è “corretta” si vede qui. Un progetto che funziona ma dove i casi d’uso importano localStorage direttamente non supera questa dimostrazione — cambiare l’archivio richiede toccare i casi d’uso.
Un progetto corretto supera la dimostrazione perché i casi d’uso ricevono l’archivio come parametro (dependency injection) e non sanno — né devono sapere — cosa c’è dentro.
Passo per passo nel progetto
- Crea
src/infrastructure/archivioInMemoria.js— un adapter che usa una variabile invece di localStorage:
// infrastructure/archivioInMemoria.js
export function creaArchivioInMemoria(datiIniziali = []) {
let dati = datiIniziali.map(d => ({ ...d }));
return {
caricaTutti() {
return dati.map(d => ({ ...d }));
},
salva(strumenti) {
dati = strumenti.map(d => ({ ...d }));
},
trovaPerId(id) {
const trovato = dati.find(d => d.id === id);
return trovato ? { ...trovato } : null;
}
};
}
- Crea
src/test/test-sostituzione-archivio.jscon cinque test che usano i casi d’uso reali:
// src/test/test-sostituzione-archivio.js
import { creaArchivioInMemoria } from "../infrastructure/archivioInMemoria.js";
import { registraStrumento } from "../application/registraStrumento.js";
import { apriPrestito } from "../application/apriPrestito.js";
import { chiudiPrestito } from "../application/chiudiPrestito.js";
import { segnalaGuasto } from "../application/segnalaGuasto.js";
import { cercaStrumenti } from "../application/cercaStrumenti.js";
function ok(desc) { console.log("OK:", desc); }
function ko(desc, e) { console.error("FALLITO:", desc, e?.message ?? e); }
// test 1: registra e cerca
try {
const archivio = creaArchivioInMemoria();
const archivioP = creaArchivioInMemoria();
registraStrumento({ nome: "Multimetro", categoria: "misura" }, archivio);
const trovati = cercaStrumenti({ filtro: "Multi" }, archivio);
if (trovati.length !== 1) throw new Error(`atteso 1, trovato ${trovati.length}`);
if (trovati[0].nome !== "Multimetro") throw new Error("nome errato");
ok("registra e cerca — archivio in memoria");
} catch (e) { ko("registra e cerca", e); }
// test 2: apri prestito su strumento disponibile
try {
const archivio = creaArchivioInMemoria([
{ id: "str-001", nome: "Oscilloscopio", categoria: "misura", stato: "disponibile" }
]);
const archivioP = creaArchivioInMemoria();
const prestito = apriPrestito({ strumentoId: "str-001" }, archivio, archivioP);
if (!prestito.id) throw new Error("prestito senza ID");
const strumento = archivio.trovaPerId("str-001");
if (strumento.stato !== "in-prestito") throw new Error("stato non aggiornato");
ok("apri prestito — archivio in memoria");
} catch (e) { ko("apri prestito", e); }
// test 3: chiudi prestito
try {
const archivio = creaArchivioInMemoria([
{ id: "str-002", nome: "Saldatore", categoria: "saldatura", stato: "in-prestito" }
]);
const archivioP = creaArchivioInMemoria([
{ id: "pre-001", strumentoId: "str-002", dataApertura: "2025-10-01", dataChiusura: null }
]);
chiudiPrestito({ strumentoId: "str-002" }, archivio, archivioP);
const strumento = archivio.trovaPerId("str-002");
if (strumento.stato !== "disponibile") throw new Error("stato non tornato a disponibile");
ok("chiudi prestito — archivio in memoria");
} catch (e) { ko("chiudi prestito", e); }
// test 4: segnala guasto
try {
const archivio = creaArchivioInMemoria([
{ id: "str-003", nome: "Trapano", categoria: "perforazione", stato: "disponibile" }
]);
segnalaGuasto({ strumentoId: "str-003" }, archivio);
const strumento = archivio.trovaPerId("str-003");
if (strumento.stato !== "guasto") throw new Error("stato non aggiornato a guasto");
ok("segnala guasto — archivio in memoria");
} catch (e) { ko("segnala guasto", e); }
// test 5: errore — strumento non trovato
try {
const archivio = creaArchivioInMemoria();
const archivioP = creaArchivioInMemoria();
apriPrestito({ strumentoId: "non-esiste" }, archivio, archivioP);
ko("errore strumento non trovato", new Error("avrebbe dovuto lanciare"));
} catch (e) {
if (e.message === "STRUMENTO_NON_TROVATO") ok("errore STRUMENTO_NON_TROVATO — archivio in memoria");
else ko("errore strumento non trovato — tipo sbagliato", e);
}
console.log("\nTutti i test eseguiti con archivio in memoria.");
console.log("I casi d'uso non sono stati modificati.");
- Esegui con
node src/test/test-sostituzione-archivio.js. - Mostra il risultato: tutti “OK”, nessuna modifica ai casi d’uso.
Esempio JS — come cambiare l’archivio in main.js per la dimostrazione
// main.js — versione con archivio localStorage (produzione)
import * as archivioStrumenti from "./src/infrastructure/archivioStrumentiLocalStorage.js";
// main.js — versione con archivio in memoria (demo di sostituzione)
import { creaArchivioInMemoria } from "./src/infrastructure/archivioInMemoria.js";
const archivioStrumenti = creaArchivioInMemoria();
// I casi d'uso sono identici in entrambi i casi:
import { apriPrestito } from "./src/application/apriPrestito.js";
// apriPrestito non sa quale archivio sta usando.
Prova di comprensione
Rispondi per iscritto:
- Cosa prova il fatto che i test passano con entrambi gli adapter?
- Se dovessi aggiungere un terzo adapter (file JSON con Node.js), cosa dovresti scrivere per passare gli stessi test?
- Cosa succederebbe se
apriPrestitousasselocalStorage.getItemdirettamente invece di riceverlo come parametro? - Qual è la differenza tra “i test passano” e “il comportamento è corretto”?
Errori da evitare
- modificare i casi d’uso per farli funzionare con l’archivio in memoria — se devi farlo, l’architettura è sbagliata;
- usare
localStoragenell’archivio in memoria — non ha senso e richiede il browser; - non copiare i dati nell’archivio in memoria — la mutazione condivisa tra test causa bug difficili da trovare.
Prodotto da consegnare
Il file src/test/test-sostituzione-archivio.js con cinque test che passano con l’archivio in memoria, senza modifiche ai casi d’uso. Come verifica per la presentazione: mostra il cambio dell’import in main.js e riesegui i test.
Checklist di chiusura
archivioInMemoria.jsrispetta il contratto con le tre funzioni;- i cinque test passano tutti con l’archivio in memoria;
- i file dei casi d’uso non sono stati toccati per questa dimostrazione;
- so cambiare l’import in
main.jse rieseguire la demo in meno di 30 secondi; - so spiegare verbalmente cosa dimostra questo test.
Risultato atteso
Ho eseguito 5 test con archivio in memoria — tutti OK.
I casi d'uso (apriPrestito, chiudiPrestito, ecc.) non sono stati modificati.
Cambio l'import in main.js da localStorage a in memoria in una riga.
Questa è la prova che l'infrastruttura è un dettaglio sostituibile.