modulo 18 di 24 / Ore 41-60
Moduli 17-24 / Checkpoint 60 ore
Validazione lato UI e lato caso d'uso
Come implementare validazione a due livelli nel Registro: UI per feedback immediato e caso d'uso per il controllo definitivo, con esempi concreti per ogni campo del form.
Validazione lato UI e lato caso d’uso
Questa lezione appartiene al Modulo 17 — Robustezza e gestione degli errori.
Obiettivo della lezione
Implementare la validazione su due livelli nel Registro: nella UI per dare feedback immediato all’utente, e nel caso d’uso per il controllo definitivo delle regole. I due livelli hanno scopi diversi e non si escludono.
Alla fine ogni campo del form del Registro ha validazione a entrambi i livelli, con messaggi appropriati per ciascuno.
Perché serve nel percorso
La validazione nella sola UI può essere aggirata (request dirette, rimozione attributi HTML, bug nel JS). La validazione nel solo caso d’uso non dà feedback immediato all’utente — l’errore arriva solo dopo il submit. I due livelli insieme garantiscono sia l’esperienza utente sia la correttezza del sistema.
Nel Registro del laboratorio
Consideriamo il form per registrare uno strumento con i campi nome e categoria.
Validazione lato UI (immediata, nel form handler):
// src/presentation/formRegistraStrumento.js
form.addEventListener("submit", (event) => {
event.preventDefault();
const nome = form.elements["nome"].value.trim();
const categoria = form.elements["categoria"].value;
// Validazione UI: controlla che i campi non siano vuoti
// (non applica le regole di business — non sa quanto deve essere lungo il nome)
if (!nome) {
mostraErroreUI(campoNome, "Inserisci il nome dello strumento.");
return;
}
if (!categoria) {
mostraErroreUI(campoCategoria, "Seleziona una categoria.");
return;
}
// Solo se la validazione UI passa, chiama il caso d'uso
try {
registraStrumento({ nome, categoria }, archivioStrumenti);
mostraSuccesso("Strumento registrato.");
form.reset();
} catch (err) {
// La validazione del caso d'uso può ancora fallire
mostraErroreUI(campoNome, traduciErrore(err.message));
}
});
function mostraErroreUI(elemento, testo) {
const errorDiv = elemento.nextElementSibling; // elemento <p class="errore"> dopo il campo
errorDiv.textContent = testo;
elemento.setAttribute("aria-invalid", "true");
}
Validazione lato caso d’uso (definitiva, nelle regole):
// src/application/registraStrumento.js
import { validaNomeStrumento, validaCategoria } from "../domain/strumento.js";
export function registraStrumento({ nome, categoria }, archivio) {
// Validazione del dominio: applica le regole di business
const nomePulito = validaNomeStrumento(nome); // lancia NOME_STRUMENTO_NON_VALIDO se sbagliato
const categoriaValida = validaCategoria(categoria); // lancia CATEGORIA_NON_VALIDA
const strumento = {
id: crypto.randomUUID(),
nome: nomePulito,
categoria: categoriaValida,
stato: "disponibile"
};
archivio.salva(strumento);
return strumento;
}
// src/domain/strumento.js
export function validaNomeStrumento(nome) {
const pulito = (nome ?? "").trim();
if (pulito.length < 2) throw new Error("NOME_STRUMENTO_NON_VALIDO");
if (pulito.length > 80) throw new Error("NOME_STRUMENTO_TROPPO_LUNGO");
return pulito;
}
export function validaCategoria(categoria) {
const categoriePossibili = ["Elettronica", "Misura", "Informatica", "Utensile"];
if (!categoriePossibili.includes(categoria)) throw new Error("CATEGORIA_NON_VALIDA");
return categoria;
}
Procedura guidata
- Identifica tutti i campi del form del Registro che richiedono validazione.
- Per ogni campo: definisci le regole UI (campo vuoto, formato base) e le regole di dominio (lunghezza, valori ammessi).
- Implementa la validazione UI nel form handler con feedback immediato.
- Implementa la validazione di dominio in funzioni pure in
src/domain/. - Chiama le funzioni di dominio nel caso d’uso.
- Testa entrambi i livelli: un campo vuoto e un valore semanticamente sbagliato.
Attività in classe
Definisci la validazione a due livelli per il form di registrazione prestito:
## Validazione form registrazione prestito
Campo: strumentoId
Validazione UI: (cosa controlla il form handler prima di chiamare il caso d'uso)
Validazione caso d'uso: (quale regola di dominio si applica)
Campo: studenteId
Validazione UI:
Validazione caso d'uso:
Poi implementa validaStudenteId nel dominio e aggiungi il test:
test("validaStudenteId — lancia STUDENTE_ID_MANCANTE per stringa vuota", () => {
expect(() => validaStudenteId("")).toThrow("STUDENTE_ID_MANCANTE");
expect(() => validaStudenteId(" ")).toThrow("STUDENTE_ID_MANCANTE");
});
Spiegazione guidata per studiare
I due livelli di validazione hanno responsabilità diverse e non si sostituiscono:
| Livello | Dove | Scopo | Cosa controlla |
|---|---|---|---|
| UI | form handler | Feedback immediato | Campo vuoto, formato base |
| Dominio | caso d’uso | Correttezza delle regole | Lunghezza, valori ammessi, regole business |
Perché la validazione UI non basta: se un utente bypassa il form (o se c’è un bug nel JS), la chiamata arriva al caso d’uso senza i campi necessari. Il caso d’uso deve proteggere le proprie regole indipendentemente da chi lo chiama.
Perché la validazione del dominio non basta: l’utente che inserisce un nome troppo corto e clicca submit deve ricevere il messaggio di errore immediatamente — non dopo che il form è stato elaborato. La validazione UI anticipa l’errore nel momento in cui è più utile.
I casi che passano la validazione UI ma falliscono nel dominio:
- nome di 1 carattere (UI controlla solo “non vuoto”, dominio controlla la lunghezza minima);
- categoria non nel set valido (l’utente può manipolare il DOM e aggiungere opzioni al select);
- nome di 200 caratteri (UI controlla solo “non vuoto”).
Questi casi arrivano al caso d’uso e vengono fermati lì — l’errore viene ritradotto dalla funzione traduciErrore e mostrato nella UI.
Passo per passo nel progetto
- Apri
src/domain/strumento.jse aggiungivalidaNomeStrumentoevalidaCategoria. - Apri
src/application/registraStrumento.jse usa le funzioni di validazione del dominio. - Apri il form handler e aggiungi la validazione UI per i campi vuoti.
- Testa il caso: nome vuoto → feedback UI immediato, form non inviato.
- Testa il caso: nome di 1 carattere → validazione UI passa, caso d’uso lancia errore, UI mostra messaggio tradotto.
- Aggiungi i test per le funzioni di dominio.
Prova di comprensione
- Perché la validazione UI controlla “campo vuoto” ma non “nome di almeno 2 caratteri”?
- Cosa succede se la validazione di dominio lancia
CATEGORIA_NON_VALIDAper una categoria che sembrava valida nella UI? - La funzione
validaNomeStrumentodeve sapere come mostrare il messaggio d’errore? - Se rimuovi la validazione UI, cosa cambia nell’esperienza utente?
Errori da evitare
- duplicare la regola di lunghezza nella UI e nel dominio — se cambia, la si aggiorna solo in un posto (il dominio);
- mettere
alert()per la validazione UI — non è accessibile e non può essere stilizzato; - fare la validazione del dominio solo nella UI — può essere aggirata;
- non testare le funzioni di validazione del dominio — contengono logica che deve essere verificata.
Prodotto da consegnare
Le funzioni di validazione in src/domain/strumento.js, il caso d’uso aggiornato per usarle, il form handler con validazione UI per i campi vuoti, e i test per le funzioni di dominio.
Checklist di chiusura
validaNomeStrumentoevalidaCategoriaesistono insrc/domain/- il caso d’uso usa le funzioni di validazione del dominio
- il form handler controlla i campi vuoti prima di chiamare il caso d’uso
- i messaggi di errore sono diversi nei due livelli (UI: “inserisci il nome”, dominio → traduciErrore → “il nome deve avere almeno 2 caratteri”)
- i test verificano sia il caso valido sia i casi di errore per ogni funzione di validazione
Risultato atteso
Ho aggiunto validaNomeStrumento e validaCategoria in src/domain/strumento.js.
Il caso d'uso registraStrumento le usa prima di salvare.
Il form handler controlla i campi vuoti con feedback immediato.
Un nome di 1 carattere passa la validazione UI ma viene fermato dal dominio — il messaggio tradotto appare nella UI.
I test verificano: nome vuoto lancia errore, nome di 1 carattere lancia errore, nome valido non lancia errore.