modulo 04 / lezione 23
Architettura mentale di un'applicazione
Frontend, backend, dati, stato: cosa vive dove
Costruire una mappa semplice delle parti di un'applicazione per evitare richieste confuse all'AI.
Frontend, backend, dati, stato: cosa vive dove
Un’applicazione moderna ha quattro strati che lavorano insieme: frontend (cosa vede l’utente), backend (cosa il server elabora), dati (cosa persiste), stato (cosa cambia in memoria). Confondere dove vive cosa è la causa numero uno di codice fragile, problemi di sicurezza e refactor dolorosi. Sapere fare la mappa prima di chiedere all’AI evita tutto questo.
Perché questa lezione conta
Quando chiedi al modello “fammi una funzione che salva le note”, senza specificare dove, il modello sceglie. Spesso male: validazione solo client (insicura), stato in localStorage (non condiviso), API call dal componente UI (accoppiamento), regole di business in JSX (impossibile testare). Senza mappa mentale chiara, ricevi sempre default plausibili ma fragili. Con mappa, dirigi il modello verso la struttura giusta.
Obiettivo operativo
Alla fine devi saper:
- distinguere frontend, backend, dati, stato e cosa vive in ognuno;
- classificare quattro tipi di stato (locale, condiviso, server, derivato);
- disegnare in 5 minuti la mappa di un’app prima di chiedere codice.
Nucleo della lezione
1. I quattro strati
Frontend (client): tutto ciò che gira nel browser/app dell’utente. Include UI, eventi (click, scroll), animazioni, validazione di aiuto, navigazione, stato locale temporaneo.
Cosa NON va qui: regole di business critiche, autorizzazioni reali, segreti, validazione affidabile, dati sensibili non strettamente necessari per la UI.
Backend (server): codice che gira su macchine controllate da te. Include logica di business, autorizzazioni reali, accesso al database, integrazione con servizi esterni, validazione affidabile, gestione segreti.
Cosa NON va qui: dettagli puramente di presentazione (es. quale colore è il bottone), stato UI temporaneo del singolo utente.
Dati (persistence): dove i dati sopravvivono ai riavvi. Database (Postgres, SQLite, MongoDB), file system, blob storage, cache distribuita. Schema, indici, vincoli di integrità.
Cosa NON va qui: dati derivabili (calcolati al volo), dati temporanei (sessioni brevi possono stare in cache, non DB), dati duplicati senza ragione.
Stato (in-memory): cosa cambia mentre l’app è viva, sia client che server. Quattro tipi (vedi sezione 2).
2. Quattro tipi di stato
Stato locale: vive in un componente o in una funzione, non condiviso. Esempio: campo input che si sta digitando, dropdown aperto/chiuso, hover.
Tecniche: useState (React), let reattivo (Svelte), ref (Vue). Resta dentro il singolo componente.
Stato condiviso (client): usato da più parti dell’UI. Esempio: utente loggato, tema (dark/light), carrello acquisti.
Tecniche: Context (React), store (Pinia/Vuex/Zustand), stores Svelte. Vive nel client ma è disponibile in tutti i componenti.
Stato server: dati che arrivano dal backend. Cache di dati remoti. Esempio: lista prodotti caricata da API, profilo utente.
Tecniche: TanStack Query, SWR, Apollo Client. Cache + invalidation + sync. Trattato come “owned dal server, replicato sul client”.
Stato derivato: calcolato da altro stato. NON va salvato a parte. Esempio: numero totale items in carrello = somma di quantità, non è uno stato indipendente.
Tecniche: computed (Vue), useMemo (React), derived (Svelte). Si ricalcola quando cambia la fonte.
Errore tipico: trattare stato derivato come stato indipendente → bug di sincronizzazione (i due divergono).
3. Errori frequenti
Cinque pattern di confusione che generano fragilità:
Logica critica nel client: “se utente è admin, mostra bottone delete, e se clicca, cancella”. Il client decide chi è admin e cosa può cancellare. Disastro: chiunque può modificare il client. Fix: client mostra/nasconde per UX, server verifica e autorizza.
Validazione solo client: form valida lato client, server accetta tutto. Bypass banale via API direct call. Fix: validazione client per UX (immediata), validazione server per sicurezza.
Dati duplicati: stessa info in 3 posti che si desincronizzano. Fix: una fonte di verità. Tutto il resto è derivato o cache.
Stato in localStorage trattato come DB: dati condivisi fra utenti salvati in localStorage di uno → invisibili agli altri. Fix: localStorage = preferenze locali individuali; condiviso = backend.
Stato server replicato a mano: invece di usare TanStack Query/SWR, fetch + setState manuale. Cache stale, sync rotta, race condition. Fix: usa libreria specializzata o pattern chiaro.
Procedura pratica
Prima di chiedere codice, mappa l’app:
- Disegna 4 box: frontend, backend, dati, servizi esterni.
- Posiziona: ogni feature/dato/regola va in uno o più box.
- Frecce: chi parla con chi (client → API → DB; client ← API ← DB; servizi esterni ← API).
- Marca stati: locale, condiviso, server, derivato.
- Identifica violazioni: regole critiche nel client, validazione assente lato server, dati duplicati, ecc.
Tempo: 10 minuti. Salva ore.
Laboratorio guidato
Disegna mappa di app concreta: lista task condivise fra coppia (es. spesa familiare).
Frontend (web/mobile):
- Lista task corrente (cache di server state)
- Form aggiunta task (stato locale)
- Toggle “completata” (UX, ma fonte di verità è server)
- Profilo utente loggato (stato condiviso client)
Backend (API):
- Endpoint
GET /tasks(filtra per coppia di utente) - Endpoint
POST /tasks(validazione: autore = utente corrente) - Endpoint
PATCH /tasks/:id(autorizzazione: solo membri della coppia possono modificare) - Logica: chi può vedere/modificare cosa
Dati (DB):
- Tabelle:
users,couples,tasks - Vincoli:
tasks.couple_idforeign key,task.completed_bypuò essere null
Servizi esterni:
- Auth provider (es. Clerk, Supabase Auth, Auth0)
- Notifiche push (opzionale)
Stati:
- form input → locale
- utente loggato → condiviso client
- lista task → server state (cache lato client via TanStack Query)
- “task non ancora completate” → derivato (filtro su lista)
Pratica: prendi l’app a cui stai pensando, disegna la stessa mappa.
Output atteso
Diagramma mentale (anche su carta o lavagna virtuale tipo Excalidraw): 4 box, frecce, etichette stato. Da allegare ai prompt iniziali per dare al modello contesto strutturale.
Studio passo passo
Studia questa lezione in tre passaggi, senza correre subito al prompt o al codice.
- Prima lettura: chiarisci l’obiettivo: costruire una mappa semplice delle parti di un’applicazione per evitare richieste confuse all’AI. Se non riesci a riscriverlo in una frase tua, rileggi il nucleo della lezione.
- Seconda lettura: trasforma i contenuti in domande operative:
- Frontend come interfaccia e interazione, backend come regole e servizi, database come memoria durevole.
- Stato locale, stato condiviso, stato server e stato derivato: differenze pratiche.
- Errori frequenti: mettere logica critica nel client, duplicare dati, nascondere stato in punti difficili da tracciare.
- Applicazione: svolgi il laboratorio: disegnare la mappa di una app esistente o immaginata segnando dove vivono dati, regole e UI. L’output da ottenere è: un diagramma minimo client/server/dati da usare come contesto prima della generazione.
Prima di passare alla lezione successiva, controlla di avere un artefatto concreto: una tabella, un prompt, una checklist, un mini report, un diff commentato o una decisione scritta. Nel vibe coding la comprensione resta astratta finché non produce una traccia verificabile.
Checklist di verifica
- so distinguere frontend, backend, dati, stato;
- so classificare uno stato in locale/condiviso/server/derivato;
- ho disegnato la mappa per il prossimo progetto;
- nessuna regola critica vive nel client del mio prossimo progetto.
Errori comuni
Saltare la mappa. “Mi è chiaro, vado al codice”. Dopo 100 righe il modello ha messo regole nel posto sbagliato. Mappa da 10 min salva tutto.
Trattare localStorage come database condiviso. Local = locale. Sempre.
Stato derivato come stato indipendente. Salvi total accanto a items. Aggiorni items, dimentichi total. Disastro. Total è derivato.
API che torna intero DB al client. “Fetch all users” sul client = leak di dati e overhead di rete. API torna ciò che serve, autorizzato per quell’utente.
UI che fa autorizzazione. Nascondere bottone non è autorizzazione. Server deve rifiutare l’azione anche se la chiamata API arriva (perché arriverà).
Prompt di lavoro
Sto progettando: [feature]
Ecco la mia mappa attuale:
Frontend:
- [componenti / responsabilità / stato locale]
Backend:
- [endpoint / logica di business / autorizzazioni]
Dati:
- [tabelle / schema / vincoli]
Servizi esterni:
- [auth / pagamenti / email / ...]
Stato:
- locale: [...]
- condiviso client: [...]
- server: [...]
- derivato: [...]
Voglio che tu:
1. Verifichi la mappa: c'è qualcosa nel posto sbagliato? logica critica nel client? validazione solo client? dati duplicati? stato derivato trattato come indipendente?
2. Segnali rischi di sicurezza prevedibili.
3. Suggerisca correzioni minime, non riscritture.
Non scrivere codice. Verifica architettura.
Esercizio finale
Prendi un’app esistente (tua o di terzi che usi). Disegna la mappa a 4 box come la immagini tu. Poi, se possibile, controlla nel codice/devtools se i tuoi assunti sono giusti. Spesso scopri che la mappa è diversa da come pensavi: regole nel posto sbagliato, dati duplicati, stato derivato salvato. Quel “scopro” è la pratica della lezione: l’occhio per la mappa cresce con esercizi reali.