modulo 22 di 24 / Ore 41-60
Moduli 17-24 / Checkpoint 60 ore
Estrarre mapping e adapter dove servono
Come identificare dove estrarre funzioni di mapping (domain→DTO, DTO→domain) e adapter (localStorage, API) nel Registro, con esempi before/after e criteri per decidere quando farlo.
Estrarre mapping e adapter dove servono
Questa lezione appartiene al Modulo 22 — Refactoring avanzato e varianti.
Obiettivo della lezione
Capire quando e dove estrarre le funzioni di mapping e gli adapter nel Registro del laboratorio scolastico. Alla fine avrai un adapter di localStorage separato dal dominio e una funzione di mapping esplicita tra oggetti di dominio e formato salvato.
Perché serve nel percorso
Senza mapping esplicito, il formato dei dati salvati in localStorage entra nel dominio: le funzioni di caso d’uso iniziano a dipendere dalla struttura JSON, e cambiare come si salvano i dati significa toccare anche le regole. L’adapter isola questo confine.
Nel Registro del laboratorio
Lo strumento ha due rappresentazioni diverse nel progetto:
- Oggetto di dominio (quello che usa
registraPrestito,cercaStrumenti, le regole):
// src/domain/strumento.js
{ id: "str-001", nome: "Oscilloscopio", categoria: "Misura", stato: "disponibile" }
- DTO salvato in localStorage (quello che scrive e legge l’adapter):
// formato JSON in localStorage["registro_strumenti"]
{ id: "str-001", name: "Oscilloscopio", category: "Misura", status: "disponibile" }
La differenza sembra piccola (nome/name, categoria/category, stato/status), ma senza mapping esplicito i casi d’uso finiscono per usare le chiavi inglesi — oppure localStorage finisce per contenere chiavi italiane. Entrambe le scelte creano accoppiamento nascosto.
Procedura guidata
- Identifica in
src/infrastructure/se esiste già un filearchivioStrumentiLocalStorage.js. - Cerca se nel file ci sono conversioni dirette tra campo italiano e campo inglese dentro le funzioni
salvaotrova. - Estrai le conversioni in due funzioni separate.
- Sposta le funzioni nel file di mapping.
- Aggiorna l’adapter per usarle.
- Verifica che il caso d’uso non cambi.
Attività in classe
Analizza questo codice — è il “prima”:
// src/infrastructure/archivioStrumentiLocalStorage.js — PRIMA
function salvaStrumento(strumento) {
const raw = JSON.parse(localStorage.getItem("registro_strumenti") || "[]");
raw.push({
id: strumento.id,
name: strumento.nome, // conversione nascosta
category: strumento.categoria,
status: strumento.stato
});
localStorage.setItem("registro_strumenti", JSON.stringify(raw));
}
function trovaTutti() {
const raw = JSON.parse(localStorage.getItem("registro_strumenti") || "[]");
return raw.map(item => ({
id: item.id,
nome: item.name, // conversione nascosta
categoria: item.category,
stato: item.status
}));
}
Il problema: la conversione è duplicata in ogni funzione dell’adapter, e se aggiungi un campo nuovo lo devi cambiare in più punti.
Estrai il mapping in un file separato — questo è il “dopo”:
// src/infrastructure/mapping/strumentoMapping.js — DOPO
export function strumentoToDTO(strumento) {
return {
id: strumento.id,
name: strumento.nome,
category: strumento.categoria,
status: strumento.stato
};
}
export function dtoToStrumento(dto) {
return {
id: dto.id,
nome: dto.name,
categoria: dto.category,
stato: dto.status
};
}
// src/infrastructure/archivioStrumentiLocalStorage.js — DOPO
import { strumentoToDTO, dtoToStrumento } from "./mapping/strumentoMapping.js";
function salvaStrumento(strumento) {
const raw = JSON.parse(localStorage.getItem("registro_strumenti") || "[]");
raw.push(strumentoToDTO(strumento));
localStorage.setItem("registro_strumenti", JSON.stringify(raw));
}
function trovaTutti() {
const raw = JSON.parse(localStorage.getItem("registro_strumenti") || "[]");
return raw.map(dtoToStrumento);
}
Compila la scheda:
## Mapping estratto
File di mapping creato:
Campi convertiti da dominio a DTO:
Campi convertiti da DTO a dominio:
Funzioni dell'adapter aggiornate:
Verifica eseguita:
Spiegazione guidata per studiare
Il mapping risolve un problema di accoppiamento: senza di esso, il formato di localStorage deve coincidere esattamente con il modello di dominio. Questo va bene finché sono la stessa cosa — ma appena divergono (perché cambi backend, aggiungi un campo, rinomini qualcosa), devi toccare il dominio per cambiare come si salvano i dati.
Con il mapping estratto, puoi cambiare strumentoToDTO senza toccare Strumento, e puoi cambiare Strumento senza toccare il formato JSON salvato.
Lo stesso schema vale per i prestiti:
// src/infrastructure/mapping/prestitoMapping.js
export function prestitoToDTO(prestito) {
return {
id: prestito.id,
tool_id: prestito.strumentoId,
borrower: prestito.studenteId,
opened_at: prestito.apertura.toISOString(),
closed_at: prestito.chiusura ? prestito.chiusura.toISOString() : null
};
}
export function dtoToPrestito(dto) {
return {
id: dto.id,
strumentoId: dto.tool_id,
studenteId: dto.borrower,
apertura: new Date(dto.opened_at),
chiusura: dto.closed_at ? new Date(dto.closed_at) : null
};
}
Nota: il formato ISO 8601 per le date è una scelta dell’infrastruttura. Il dominio usa oggetti Date. Questo confine è esplicito e documentato nel mapping.
Passo per passo nel progetto
- Crea la cartella
src/infrastructure/mapping/se non esiste. - Crea
strumentoMapping.jsconstrumentoToDTOedtoToStrumento. - Crea
prestitoMapping.jsconprestitoToDTOedtoToPrestito. - Apri
archivioStrumentiLocalStorage.jse sostituisci le conversioni inline con le funzioni di mapping. - Apri
archivioPrestitiLocalStorage.jse fai lo stesso. - Esegui i test esistenti: devono passare senza modifiche ai casi d’uso.
- Aggiungi un test sul mapping stesso:
// test/mapping/strumentoMapping.test.js
import { strumentoToDTO, dtoToStrumento } from "../../src/infrastructure/mapping/strumentoMapping.js";
test("strumentoToDTO converte le chiavi da italiano a inglese", () => {
const strumento = { id: "s1", nome: "Trapano", categoria: "Utensile", stato: "disponibile" };
const dto = strumentoToDTO(strumento);
expect(dto.name).toBe("Trapano");
expect(dto.category).toBe("Utensile");
expect(dto.status).toBe("disponibile");
expect(dto.nome).toBeUndefined(); // nessuna chiave italiana nel DTO
});
test("dtoToStrumento è l'inverso di strumentoToDTO", () => {
const originale = { id: "s1", nome: "Trapano", categoria: "Utensile", stato: "disponibile" };
const dto = strumentoToDTO(originale);
const ricostruito = dtoToStrumento(dto);
expect(ricostruito).toEqual(originale);
});
Prova di comprensione
Rispondi per iscritto:
- Perché estrarre il mapping in un file separato invece di tenerlo dentro l’adapter?
- Cosa succede se aggiungi il campo
serialeaStrumento— in quanti posti devi intervenire con il mapping estratto? - Il dominio deve conoscere il file
strumentoMapping.js? Perché? - Come verifichi che il mapping non ha perso informazioni?
Errori da evitare
- mettere il mapping direttamente nel caso d’uso (
registraPrestitonon deve sapere come si chiama il campo in localStorage); - usare le chiavi del DTO come nomi nel dominio per evitare il mapping (vince il dominio, non il formato di salvataggio);
- dimenticare di gestire i valori
nullnel mapping (ad esempiochiusura: nullnei prestiti aperti); - non testare il mapping: è logica, non configurazione, e può sbagliare.
Prodotto da consegnare
Due file di mapping (strumentoMapping.js e prestitoMapping.js) con le funzioni di conversione, gli adapter aggiornati per usarli, e almeno un test che verifica il round-trip (domain → DTO → domain).
Checklist di chiusura
strumentoToDTOedtoToStrumentosono in un file separato dall’adapterprestitoToDTOedtoToPrestitoesistono e gestiscono le date- nessun adapter contiene conversioni di campo inline
- nessun caso d’uso importa funzioni di mapping
- i test esistenti passano senza modifiche
- il README segnala l’esistenza della cartella
mapping/
Risultato atteso
Ho estratto strumentoMapping.js e prestitoMapping.js in src/infrastructure/mapping/.
L'adapter di localStorage usa queste funzioni per convertire tra oggetti di dominio e JSON.
I casi d'uso non sono stati modificati.
Il test del round-trip dimostra che il mapping non perde dati.
Il prossimo passo sarebbe aggiungere gestione degli errori per DTO malformati.