modulo 12 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Separare messaggio pubblico e dettaglio tecnico

Costruire nel Registro un sistema a due livelli per gli errori: il messaggio pubblico che aiuta l'utente e il dettaglio tecnico che aiuta chi mantiene il sistema.

25 minArchitettura del software

Separare messaggio pubblico e dettaglio tecnico

Questa lezione appartiene al Modulo 12 — Errori, validazione e messaggi.

I due destinatari di ogni errore

Quando un’operazione fallisce nel Registro, ci sono due destinatari con bisogni opposti:

L’utente vuole sapere:

  • cosa è andato storto in termini comprensibili
  • cosa può fare per risolvere

Chi mantiene il sistema vuole sapere:

  • quale parte del codice ha generato il problema
  • quali dati erano coinvolti
  • se è un bug o una condizione attesa

Questi bisogni richiedono informazioni diverse. Il messaggio pubblico deve essere semplice. Il dettaglio tecnico deve essere preciso. Non si possono unire senza compromettere entrambi.

Il flusso di separazione nel Registro

Domain/Application  → lancia errore con codice preciso
                         throw new Error("STRUMENTO_NON_TROVATO")

Presentation        → cattura l'errore
                      → traduce il codice in messaggio utente
                      → conserva il dettaglio tecnico nel log

In codice:

// Presentation — gestisce la separazione
async function gestisciSubmitPrestito(comando) {
  try {
    await registraPrestito(comando);
    mostraSuccesso("Prestito registrato.");
  } catch (errore) {
    // Messaggio pubblico — per l'utente
    const messaggioUtente = traduciErrore(errore.message);
    mostraErroreUI(messaggioUtente);

    // Dettaglio tecnico — per chi mantiene il sistema
    if (!messaggiUtente[errore.message]) {
      // Errore non previsto: logga il dettaglio completo
      console.error("[ERRORE NON PREVISTO]", {
        codice: errore.message,
        stack: errore.stack,
        comando: { strumentoId: comando.strumentoId }  // no dati personali
      });
    }
  }
}

La funzione di traduzione

La traduzione da codice a messaggio è una responsabilità della Presentation:

// presentation/traduttoreErrori.js
const messaggiUtente = {
  // Errori applicativi
  STRUMENTO_NON_TROVATO:        "Strumento non trovato. Controlla l'ID e riprova.",
  PRESTITO_NON_TROVATO:         "Prestito non trovato.",
  // Errori di dominio
  STRUMENTO_NON_DISPONIBILE:    "Questo strumento non può essere prestato al momento.",
  STRUMENTO_GIA_IN_PRESTITO:    "Questo strumento è già in prestito.",
  STRUMENTO_GUASTO_NON_PRESTABILE: "Questo strumento è guasto e non può essere prestato.",
  PRESTITO_GIA_CHIUSO:          "Questo prestito è già stato chiuso.",
  // Errori di input
  NOME_NON_VALIDO:              "Il nome deve avere almeno 2 caratteri.",
  CATEGORIA_MANCANTE:           "Seleziona una categoria.",
  // Errori infrastrutturali
  ARCHIVIO_NON_DISPONIBILE:     "Non riesco a salvare i dati. Riprova tra poco.",
};

function traduciErrore(codice) {
  return messaggiUtente[codice] ?? "Si è verificato un problema. Riprova tra poco.";
}

Cosa va nel messaggio pubblico

Il messaggio pubblico deve:

  • usare parole del vocabolario dell’utente, non del codice
  • dire cosa può fare l’utente (“controlla l’ID”, “riprova tra poco”)
  • non rivelare stack trace, nomi di variabili, chiavi di localStorage
  • essere breve — una frase basta
✅ "Strumento non trovato. Controlla l'ID e riprova."
✅ "Questo strumento è guasto e non può essere prestato."
✅ "Non riesco a salvare i dati. Riprova tra poco."

❌ "STRUMENTO_NON_TROVATO"
❌ "Cannot read properties of undefined (reading 'stato')"
❌ "localStorage.getItem('registro:strumenti') ha restituito null"

Cosa va nel dettaglio tecnico

Il dettaglio tecnico deve:

  • essere preciso e completo per il debugging
  • finire in console.error (non in alert o nell’UI)
  • includere il contesto minimo utile (ID coinvolti, operazione)
  • non includere dati personali (nomi di studenti, assegnatari)
// ✅ Dettaglio tecnico utile
console.error("Errore non previsto in registraPrestito", {
  codice: errore.message,
  strumentoId: comando.strumentoId,
  timestamp: new Date().toISOString()
});

// ❌ Dettaglio tecnico con dati personali — da evitare
console.error("Errore", {
  assegnatario: comando.assegnatario,  // dato personale
  command: JSON.stringify(comando)     // potrebbe contenere dati sensibili
});

Il caso del messaggio generico

Quando si cattura un errore non previsto (codice non nel dizionario), il messaggio generico è la scelta corretta:

function traduciErrore(codice) {
  const messaggio = messaggiUtente[codice];
  if (messaggio) return messaggio;

  // Errore non previsto — messaggio generico per l'utente
  // Il dettaglio verrà loggato da chi chiama questa funzione
  return "Si è verificato un problema. Riprova tra poco.";
}

Il messaggio generico non è una sconfitta — è la scelta corretta per errori imprevisti. Meglio un messaggio vago ma sicuro che uno specifico ma che rivela dettagli interni.

Procedura guidata

  1. Apri tutti i blocchi catch del tuo Registro.
  2. Per ogni catch: cosa viene mostrato all’utente? È un messaggio pubblico o un dettaglio tecnico?
  3. Aggiungi traduciErrore se non esiste.
  4. Aggiungi console.error per i dettagli tecnici degli errori non previsti.
  5. Verifica che nessun codice errore (STRUMENTO_NON_TROVATO) appaia nell’interfaccia.

Attività in classe

## Separazione errori nel mio Registro

Blocco catch analizzato (file:riga):

Cosa mostra all'utente attualmente:
  → è un messaggio pubblico o un dettaglio tecnico?

Messaggio pubblico proposto:

Il dettaglio tecnico va in console.error (sì/no):

Errori non previsti hanno un messaggio generico (sì/no):

Dati personali nei log (da rimuovere se presenti):

Spiegazione guidata per studiare

La separazione tra messaggio pubblico e dettaglio tecnico segue la stessa logica della separazione dei livelli: ogni parte del sistema ha una responsabilità diversa verso destinatari diversi.

Il punto centrale è: il codice errore è un contratto interno — non una comunicazione con l’utente. STRUMENTO_NON_TROVATO è preciso e utile per il debugging. Non ha senso per un tecnico del laboratorio scolastico. La traduzione avviene nella Presentation — l’unico livello che conosce sia il codice che il contesto dell’interfaccia.

Il rischio tipico è usare alert(errore.message) perché è rapido. Funziona in sviluppo. In produzione espone i dettagli interni e confonde l’utente.

Passo per passo nel progetto

  1. Cerca tutti i punti che mostrano errori: alert, textContent = errore, innerHTML = errore.message.
  2. Per ognuno: sostituisci con mostraErroreUI(traduciErrore(errore.message)).
  3. Aggiungi console.error nello stesso blocco per gli errori non previsti.
  4. Verifica il dizionario: copre tutti i codici errore che il progetto genera?

Esempio da leggere lentamente

// Prima: un solo catch che mostra il codice grezzo
catch (errore) {
  alert(errore.message); // ← mostra "STRUMENTO_NON_TROVATO" all'utente
}

// Dopo: separazione netta
catch (errore) {
  // Messaggio pubblico — comprensibile
  mostraErroreUI(traduciErrore(errore.message));

  // Dettaglio tecnico — per il debugging
  if (!messaggiUtente[errore.message]) {
    // Solo per errori non previsti — quelli previsti non hanno stack trace utili
    console.error("Errore non gestito:", errore.message, errore.stack);
  }
}

Prova di comprensione

  • Quale codice errore del tuo Registro non ha ancora un messaggio pubblico nel dizionario?
  • Dove nel tuo progetto appare un codice errore direttamente nell’interfaccia?
  • Come distingui un errore “previsto” (nel dizionario) da uno “non previsto”?

Errori da evitare

  • usare il messaggio generico per tutti gli errori — impossibile distinguere cosa è successo;
  • mostrare lo stack trace nell’interfaccia — inutile per l’utente, rivela dettagli interni;
  • non loggare gli errori non previsti — rende il debugging impossibile;
  • inserire dati personali nei log tecnici.

Prodotto da consegnare

Può essere:

  • il dizionario messaggiUtente completo per tutti i codici errore del Registro;
  • tutti i blocchi catch aggiornati con messaggio pubblico + console.error;
  • un test manuale: provoca tre errori diversi e verifica che l’UI mostri messaggi comprensibili.

Checklist di chiusura

  • Il dizionario messaggiUtente copre tutti i codici errore del progetto
  • Nessun codice errore appare direttamente nell’interfaccia utente
  • Gli errori non previsti hanno console.error con il dettaglio tecnico
  • I log tecnici non contengono dati personali
  • Il messaggio generico è usato solo per errori non previsti

Domande per la revisione

  • Hai trovato codici errore che mancavano nel dizionario?
  • I messaggi pubblici dicono all’utente cosa fare (non solo cosa è andato storto)?
  • Gli errori non previsti sono loggati in modo utile per il debugging?

Risultato atteso

Alla fine lo studente deve saper dire:

Nel mio Registro, gli errori vengono gestiti così:
- il dominio lancia: [codici errore]
- la Presentation traduce con messaggiUtente: [lista]
- i dettagli tecnici vanno in console.error
- i dati personali non appaiono nei log

Se questa spiegazione non è possibile, la lezione non è ancora davvero conclusa.