modulo 09 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Il problema: cambiare archivio senza riscrivere i casi d'uso

Capire perché un caso d'uso non deve dipendere direttamente da un archivio concreto se vogliamo cambiare persistenza senza riscrivere il progetto.

25 min Architettura del software

Il problema: cambiare archivio senza riscrivere i casi d’uso

Al checkpoint 20 ore abbiamo una versione essenziale del Registro del laboratorio.

Funziona con un archivio in memoria.

Questo va bene per iniziare, ma ha un limite evidente:

se ricarico la pagina, i dati spariscono.

Prima o poi vorremo passare a:

  • localStorage;
  • file JSON;
  • database;
  • API.

La domanda del modulo e:

Come faccio a cambiare archivio senza riscrivere i casi d'uso?

Obiettivo

Alla fine devi capire:

  • perche dipendere da un archivio concreto rende il codice rigido;
  • che cosa significa sostituire una infrastruttura;
  • perche i casi d’uso devono chiedere operazioni, non dettagli tecnici;
  • quali parti del progetto devono restare stabili quando cambia la persistenza.

Il caso fragile

Immagina un caso d’uso scritto cosi:

export function registraStrumento(comando) {
  const strumenti = JSON.parse(localStorage.getItem("strumenti") ?? "[]");

  const strumento = creaStrumento(comando);

  strumenti.push(strumento);
  localStorage.setItem("strumenti", JSON.stringify(strumenti));

  return strumento;
}

Funziona.

Pero RegistraStrumento ora conosce:

  • localStorage;
  • JSON;
  • la chiave "strumenti";
  • il formato di salvataggio.

Il caso d’uso non sta solo coordinando l’operazione. Sta anche decidendo un dettaglio di infrastruttura.

Che cosa succede se cambia archivio

Se vogliamo passare da localStorage a una API, dovremmo modificare il caso d’uso:

await fetch("/api/strumenti", {
  method: "POST",
  body: JSON.stringify(strumento),
});

Se poi passiamo a un file JSON, cambiamo di nuovo.

Se poi usiamo un database, cambiamo ancora.

Il problema non e cambiare una riga.

Il problema e che ogni scelta tecnica entra nei casi d’uso e li sporca.

Cosa deve sapere un caso d’uso

Un caso d’uso come RegistraStrumento ha bisogno di una cosa semplice:

salvare uno strumento

Non ha bisogno di sapere:

  • se lo strumento finisce in un array;
  • se viene serializzato in JSON;
  • se viene scritto in localStorage;
  • se viene mandato a una API;
  • se viene salvato in database.

Questi sono dettagli.

La frase chiave

Nel nostro corso useremo questa idea:

Il caso d'uso dipende da cio che gli serve, non da come viene realizzato.

Esempio:

Mi serve un archivio strumenti.
Deve permettere di salvare, cercare per id e leggere tutti gli strumenti.
Non mi interessa se dentro usa memoria, localStorage o API.

Questa e la base del concetto di interfaccia.

Prima forma del problema

Nel Modulo 8 abbiamo scritto archivi in memoria.

Esempio:

const archivioStrumenti = creaArchivioStrumentiInMemoria();

Il caso d’uso puo usarlo cosi:

archivioStrumenti.salva(strumento);
archivioStrumenti.trovaPerId(id);
archivioStrumenti.tutti();

Queste operazioni sono piu importanti dell’implementazione.

La vera domanda diventa:

Quali operazioni deve garantire qualsiasi archivio strumenti?

Archivio come dettaglio sostituibile

Vogliamo arrivare a questa situazione:

const archivioStrumenti = creaArchivioStrumentiInMemoria();

oppure:

const archivioStrumenti = creaArchivioStrumentiLocalStorage();

oppure:

const archivioStrumenti = creaArchivioStrumentiApi();

E il caso d’uso resta uguale:

const registraStrumento = creaRegistraStrumento({
  archivioStrumenti,
});

Questo e il risultato che vogliamo.

Cosa deve restare stabile

Quando cambiamo archivio, non vogliamo modificare:

  • dominio;
  • regole;
  • casi d’uso;
  • test principali del comportamento;
  • UI, se i dati restituiti hanno la stessa forma.

Potremmo dover modificare:

  • file di infrastructure;
  • punto di composizione;
  • test specifici dell’archivio;
  • gestione di errori tecnici.

Questa e una buona separazione.

Esercizio

Prendi il tuo progetto e cerca:

localStorage
JSON.parse
JSON.stringify
fetch
strumenti.push
prestiti.push

Per ogni occorrenza chiediti:

Questa riga sta in un caso d'uso?
Sta nella UI?
Sta in infrastructure?

Se localStorage o JSON.parse stanno in Application o Domain, annota il problema.

Non devi correggere tutto subito.

Devi vedere dove la persistenza concreta e entrata nel codice.

Scheda di analisi

Compila:

# Cambiare archivio

Archivio attuale:

Operazioni usate dai casi d'uso:
- ...

Dettagli concreti presenti nei casi d'uso:
- ...

Dettagli concreti presenti nella UI:
- ...

File che dovrebbero stare in Infrastructure:
- ...

Primo cambio che vorrei poter fare:

Cosa produrre

Alla fine della lezione devi avere:

  • una lista dei punti in cui il codice dipende da un archivio concreto;
  • una lista delle operazioni che un archivio deve offrire;
  • almeno un esempio di caso d’uso troppo legato alla persistenza;
  • una domanda guida scritta nel README o negli appunti: “come cambio archivio senza cambiare il caso d’uso?”.

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 il problema: cambiare archivio senza riscrivere i casi d’uso, 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 perche localStorage dentro un caso d’uso e un problema;
  • so distinguere operazione richiesta e dettaglio tecnico;
  • ho individuato quali metodi servono all’archivio;
  • so cosa dovrebbe restare stabile quando cambio persistenza;
  • ho capito il problema che l’interfaccia risolve.

Nella prossima lezione distinguiamo dipendenza concreta e dipendenza da contratto.