modulo 10 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Adapter localStorage e adapter JSON

Usare adapter semplici per collegare il contratto degli archivi a localStorage o a un file JSON senza cambiare i casi d'uso.

35 min Architettura del software

Adapter localStorage e adapter JSON

Un adapter e un pezzo di codice che collega due forme diverse.

Nel nostro progetto:

contratto ArchivioStrumenti
  <-> localStorage

oppure:

contratto ArchivioStrumenti
  <-> file JSON

Non serve trasformarlo in teoria.

Un adapter serve quando vogliamo far parlare il nostro codice con un dettaglio esterno mantenendo stabile il contratto.

Obiettivo

Alla fine devi avere:

  • un adapter localStorage;
  • una bozza di adapter JSON;
  • casi d’uso invariati;
  • punto di composizione che sceglie l’adapter;
  • consapevolezza dei limiti di ogni adapter.

Adapter localStorage

Il nostro ArchivioStrumentiLocalStorage e gia un adapter.

Perche espone:

salva
trovaPerId
tutti

ma dentro usa:

localStorage
JSON.stringify
JSON.parse
mapping

Il caso d’uso vede il contratto.

L’adapter nasconde il dettaglio.

Schema

RegistraPrestito
  usa ArchivioStrumenti

ArchivioStrumentiLocalStorage
  implementa il contratto
  usa localStorage
  usa mapping

Questa e la separazione che ci interessa.

Adapter JSON

In un progetto browser puro non possiamo sempre scrivere file JSON locali senza strumenti aggiuntivi.

Pero possiamo simulare o preparare un adapter JSON.

Esempio in ambiente Node o in una traccia didattica:

export function creaArchivioStrumentiJson({ leggiFile, scriviFile }) {
  return {
    tutti() {
      const dati = leggiFile();
      return dati.map(dtoToStrumento);
    },

    trovaPerId(id) {
      return this.tutti().find((strumento) => strumento.id === id) ?? null;
    },

    salva(strumento) {
      const strumenti = this.tutti();
      const indice = strumenti.findIndex((item) => item.id === strumento.id);

      if (indice === -1) {
        strumenti.push(strumento);
      } else {
        strumenti[indice] = strumento;
      }

      scriviFile(strumenti.map(strumentoToDto));
      return strumento;
    },
  };
}

Qui leggiFile e scriviFile sono funzioni passate dall’esterno.

Non introduciamo dettagli del file system nei casi d’uso.

Adapter API

Piu avanti potremmo avere:

export function creaArchivioStrumentiApi({ clientHttp }) {
  return {
    async tutti() {
      const dtoList = await clientHttp.get("/strumenti");
      return dtoList.map(dtoToStrumento);
    },

    async trovaPerId(id) {
      const dto = await clientHttp.get(`/strumenti/${id}`);
      return dto ? dtoToStrumento(dto) : null;
    },

    async salva(strumento) {
      const dto = strumentoToDto(strumento);
      await clientHttp.put(`/strumenti/${strumento.id}`, dto);
      return strumento;
    },
  };
}

Questa versione introduce asincronia.

Non la affrontiamo ora in modo completo.

La citiamo per capire che il contratto potrebbe cambiare se l’archivio diventa remoto.

Per il percorso browser/localStorage restiamo sincroni.

Scegliere l’adapter nel punto di composizione

In main.js:

const archivioStrumenti = creaArchivioStrumentiLocalStorage();

Oppure:

const archivioStrumenti = creaArchivioStrumentiInMemoria();

La scelta sta nel punto di composizione.

Non dentro RegistraPrestito.

Limiti da dichiarare

Adapter localStorage:

  • semplice;
  • funziona nel browser;
  • non condivide dati tra dispositivi;
  • ha limiti di spazio;
  • i dati possono essere modificati manualmente.

Adapter JSON:

  • utile per esempi o ambiente Node;
  • richiede lettura/scrittura file;
  • nel browser puro non e diretto;
  • puo essere comodo per esercizi.

Adapter API:

  • permette dati condivisi;
  • richiede backend;
  • introduce rete, errori, autorizzazioni e asincronia.

Non serve usare tutti.

Serve capire che sono sostituibili se rispettano il contratto.

Esercizio

Compila una tabella:

| Adapter | Dove salva | Vantaggio | Limite | Cambia il caso d'uso? |
| --- | --- | --- | --- | --- |
| In memoria | array | semplice | perde dati | no |
| localStorage | browser | persiste dopo refresh | solo locale | no |
| JSON | file | leggibile | richiede file system | no |
| API | server | condivisibile | asincronia e rete | forse |

Poi scegli quale adapter usare nella versione completa.

Cosa produrre

Alla fine della lezione devi avere:

  • adapter localStorage funzionante o descritto;
  • bozza di adapter JSON;
  • tabella dei limiti;
  • punto di composizione aggiornato;
  • nota su cosa cambierebbe con API.

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 adapter localStorage e adapter JSON, il punto centrale è questo: L’infrastruttura è il bordo tecnico: salva, legge, chiama API, usa localStorage o file JSON. È importante, ma deve restare sostituibile.

Il rischio tipico è far entrare il formato salvato dentro il dominio. Un oggetto comodo per localStorage non è automaticamente il modello con cui ragiona il progetto. 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. Distingui modello di dominio e formato salvato.
  4. Crea una funzione di mapping leggibile.
  5. Gestisci dati mancanti o non validi.
  6. Verifica che i casi d’uso non cambino quando cambi archivio.
  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 strumentoToDTO(strumento) {
  return {
    id: strumento.id,
    name: strumento.nome,
    category: strumento.categoria,
    status: strumento.stato
  };
}

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:

  • so spiegare cosa fa un adapter;
  • localStorage e nascosto dietro il contratto;
  • il caso d’uso non conosce JSON;
  • so dove scegliere l’adapter;
  • so quali limiti ha localStorage;
  • so perche una API richiederebbe nuove attenzioni.

Nella prossima lezione chiudiamo con una micro-sfida: cambiare archivio senza modificare il caso d’uso.