modulo 03 di 24 / Ore 0-20

Moduli 1-8 / Checkpoint 20 ore

Confini: utente, interfaccia, regole e dati salvati

Imparare a disegnare i confini del sistema distinguendo utente, interfaccia, regole del dominio, casi d'uso e dati salvati.

20 minArchitettura del software

Confini: utente, interfaccia, regole e dati salvati

Un confine è una linea che separa responsabilità diverse.

In un progetto piccolo i confini non sono sempre visibili. Il codice può stare tutto in main.js, quindi sembra che tutto appartenga allo stesso posto.

Ma anche quando non li vedi, i confini esistono.

Nel Registro del laboratorio possiamo distinguere almeno queste aree:

Utente
Interfaccia
Casi d'uso
Regole
Dati salvati

Il problema del programma fragile è che queste aree si toccano senza controllo.

Questa lezione serve a disegnare quei confini prima di spostare codice.

Obiettivo della lezione

Alla fine devi avere:

  • una mappa del flusso di una operazione del Registro;
  • almeno tre esempi di confine confuso nel codice fragile;
  • una distinzione chiara tra UI, caso d’uso, dominio e persistenza;
  • una prima idea di quali collegamenti sono accettabili e quali sono pericolosi.

Il risultato non è una nuova cartella. È una mappa.

Dal comportamento al confine

Prendiamo un’azione concreta:

Registrare un prestito

Dal punto di vista dell’utente, l’azione è semplice:

  1. scrive l’identificativo dello strumento;
  2. scrive il nome della persona che lo prende;
  3. preme un pulsante;
  4. legge un messaggio.

Dal punto di vista del programma, invece, succedono più passaggi:

  1. la UI legge i campi;
  2. un caso d’uso coordina l’operazione;
  3. il dominio verifica le regole;
  4. l’infrastruttura legge e salva dati;
  5. la UI mostra il risultato.

Il confine serve a non confondere questi passaggi.

Il confine tra utente e interfaccia

L’utente non chiama funzioni.

L’utente:

  • scrive testo;
  • seleziona uno stato;
  • clicca un bottone;
  • guarda una lista;
  • legge un messaggio.

La UI traduce queste azioni in dati per il programma.

Esempio:

const comando = {
  strumentoId: inputStrumentoId.value,
  nomePersona: inputPersona.value,
};

Questa parte appartiene alla Presentation.

Sta trasformando input HTML in un oggetto JavaScript.

Fin qui va bene.

Il problema nasce quando subito dopo la UI inizia anche a decidere regole, modificare array e salvare dati.

Il confine tra interfaccia e caso d’uso

La UI può dire:

Voglio registrare questo prestito.

Non dovrebbe però gestire tutti i passaggi interni dell’operazione.

Una UI più ordinata prepara un comando e chiama un caso d’uso:

const esito = registraPrestito({
  strumentoId: inputStrumentoId.value,
  nomePersona: inputPersona.value,
});

mostraMessaggio(esito.messaggio);

Questo esempio è ancora semplice, ma mostra un confine:

  • la UI raccoglie input;
  • il caso d’uso decide il flusso;
  • la UI mostra l’esito.

La UI non deve sapere come viene cercato lo strumento, come viene creato il prestito o dove vengono salvati i dati.

Il confine tra caso d’uso e dominio

Un caso d’uso coordina.

Il dominio decide le regole.

Per RegistraPrestito, il caso d’uso può fare questo:

1. riceve strumentoId e nomePersona
2. chiede all'archivio lo strumento
3. chiede al dominio se lo strumento può essere prestato
4. crea o aggiorna i dati necessari
5. salva le modifiche
6. restituisce un risultato

La regola:

uno strumento guasto non può essere prestato

non deve nascere nel bottone e non deve dipendere dal salvataggio.

Deve stare nel dominio, perché riguarda il significato del problema.

Il confine tra dominio e dati salvati

Una regola deve valere indipendentemente da dove sono salvati i dati.

La regola “uno strumento guasto non può essere prestato” deve restare vera:

  • con dati in memoria;
  • con localStorage;
  • con un file JSON;
  • con un database;
  • con una API.

Se per verificare questa regola devi usare per forza localStorage, il confine è confuso.

localStorage è un dettaglio tecnico.

La regola è parte del dominio.

Questa distinzione sarà importantissima quando cambieremo infrastruttura senza riscrivere i casi d’uso.

Mappa del flusso desiderato

Per il prestito, la mappa desiderata può essere questa:

Utente
  clicca "Registra prestito"
    |
    v
Presentation / UI
  legge gli input e prepara un comando
    |
    v
Application / Caso d'uso
  coordina RegistraPrestito
    |
    v
Domain / Regole
  verifica se lo strumento può essere prestato
    |
    v
Infrastructure / Archivio
  legge e salva strumenti e prestiti
    |
    v
Presentation / UI
  mostra un messaggio comprensibile

Questa mappa non dice che il codice debba essere lungo.

Dice che ogni responsabilità ha un posto riconoscibile.

Mappa del programma fragile

Nel programma fragile, invece, spesso succede questo:

Click del bottone
  legge input
  cerca nello stesso array globale
  controlla regole
  modifica stato
  crea prestito
  salva in localStorage
  costruisce messaggio
  aggiorna HTML

Non c’è un singolo errore evidente. Il problema è l’accumulo.

Più responsabilità si accumulano nello stesso punto, più diventa difficile cambiare una cosa senza toccarne altre.

Segnali di confine rotto

Un confine è probabilmente rotto quando:

  • un event listener contiene una regola importante;
  • una funzione di ricerca legge direttamente input HTML;
  • una regola usa localStorage;
  • un messaggio utente viene costruito dentro una funzione di dominio;
  • un caso d’uso usa document.querySelector;
  • cambiare il salvataggio obbliga a modificare la UI;
  • testare una regola richiede il browser;
  • lo stesso stato viene scritto in forme diverse, per esempio "guasto" e "GUASTO";
  • un file chiamato main.js contiene tutto il comportamento del sistema.

Questi segnali non sono una condanna. Sono indizi.

Servono a scegliere dove intervenire nei refactoring successivi.

Collegamenti accettabili e collegamenti pericolosi

Non tutti i collegamenti tra parti del sistema sono uguali.

Alcuni sono normali:

Collegamento Perché è accettabile
UI chiama un caso d’uso l’utente avvia un’operazione
Caso d’uso usa regole di dominio il flusso deve rispettare le regole
Caso d’uso usa un archivio deve leggere e salvare dati
Infrastructure trasforma dati salvati deve adattare formati esterni

Altri sono pericolosi:

Collegamento Perché crea fragilità
Domain usa localStorage le regole dipendono da un dettaglio tecnico
Domain usa document.querySelector le regole dipendono dalla pagina
UI decide regole di prestito la regola si duplica facilmente
Infrastructure mostra messaggi utente il salvataggio conosce la UI

Questa è una delle idee più importanti del corso: non basta separare i nomi, bisogna controllare le direzioni dei collegamenti.

Esempio: messaggio e regola sono cose diverse

Questa frase è una regola:

uno strumento guasto non può essere prestato

Questo è un messaggio per l’utente:

Non puoi registrare il prestito: lo strumento è segnato come guasto.

Sembrano simili, ma non sono la stessa cosa.

La regola decide se l’operazione è valida.

Il messaggio spiega all’utente cosa è successo.

Se li mescoli, diventa difficile cambiare il tono del messaggio senza toccare la regola. Diventa anche difficile testare la regola senza controllare testi della UI.

Attività guidata

Scegli una sola operazione del tuo Registro fragile:

  • registrare uno strumento;
  • cercare strumenti;
  • registrare un prestito;
  • chiudere un prestito;
  • aprire una segnalazione di guasto.

Disegna la mappa attuale:

Operazione scelta:

Utente:

UI:

Caso d'uso:

Regole:

Dati salvati:

Messaggi:

Poi aggiungi una seconda sezione:

Confini confusi:

1.
2.
3.

Vincolo: devi citare punti reali del codice, non frasi generiche.

Micro-sfida

Trova tre punti in cui la UI attraversa un confine.

Formato richiesto:

Punto 1
Codice o blocco: event listener "Registra prestito"
Confine attraversato: UI -> Domain
Perché: il click decide che uno strumento guasto non può essere prestato.
Dove dovrebbe andare: regola di dominio.

Ripeti lo schema per tre esempi.

Prodotto da ottenere

Alla fine della lezione devi avere:

  • una mappa del flusso attuale di una operazione;
  • una mappa del flusso desiderato;
  • tre confini confusi individuati;
  • una distinzione scritta tra regola e messaggio;
  • una nota su quale confine vuoi sistemare per primo.

Domande di comprensione

Rispondi per iscritto:

  • Che cosa separa un confine?
  • Perché la UI non dovrebbe possedere le regole principali?
  • Perché il dominio non dovrebbe conoscere localStorage?
  • Che differenza c’è tra regola e messaggio utente?
  • Quale confine del tuo progetto è più confuso oggi?

Controllo finale

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

  • so distinguere utente, UI, casi d’uso, regole e dati salvati;
  • ho disegnato una mappa del programma fragile;
  • ho trovato almeno tre confini confusi;
  • so spiegare perché localStorage non deve influenzare le regole;
  • so dire cosa dovrebbe fare la UI e cosa non dovrebbe fare;
  • capisco che i confini servono a rendere il cambiamento meno rischioso.

Nel prossimo passo parleremo proprio di cambiamento: alcune decisioni sono facili da modificare, altre diventano costose.