Schema concettuale dell’architettura di un progetto React basata su responsabilità e composizione

Architettura React: come strutturare un progetto (e perché non è MVC)

React non è MVC e non impone una struttura: come organizzare un progetto per feature e responsabilità, dove mettere lo stato ed evitare i modelli mentali importati.

Quando si parla di architettura React, quasi tutti i problemi nascono da un errore di partenza:

👉 organizzare il progetto React copiando modelli mentali che non gli appartengono.

Cartelle controllers, models, views.
Oppure separazioni forzate per “tipo di file”.
O peggio: una struttura che cresce per imitazione, non per necessità.

Il risultato è sempre lo stesso:

  • codice frammentato
  • responsabilità confuse
  • onboarding lento
  • refactor costosi

Il problema non è la struttura in sé.
È il criterio con cui la scegli.

In breve: React non è MVC e non impone una struttura. Un progetto React regge nel tempo quando lo organizzi per feature e responsabilità, tieni lo stato vicino a chi lo usa e lasci che la UI sia una funzione dello stato.

In questa guida:

Confronto concettuale tra architettura React e modello MVC tradizionale

Perché React non è MVC

Uno degli errori più comuni (e più costosi) quando si lavora con React è cercare di incastrarlo dentro il modello MVC.

Succede quasi sempre a chi:

  • viene da backend tradizionale
  • ha studiato architetture “classiche”
  • cerca ordine replicando schemi già conosciuti

Il risultato è prevedibile:

  • componenti che fanno troppe cose
  • stato sparso
  • logica difficile da seguire
  • refactor dolorosi

👉 Il problema non è React.
👉 Il problema è forzare un modello che React non è.

Perché MVC è così difficile da abbandonare

MVC (Model–View–Controller) ha funzionato bene per anni perché risolveva un problema preciso:

separare responsabilità in sistemi server-centrici

Ma React nasce per un contesto diverso:

  • UI dinamiche
  • stato che cambia spesso
  • rendering reattivo
  • flussi asincroni continui

Applicare MVC a React è come usare una mappa di carta per guidare un drone:
ti dà l’illusione di controllo, ma non descrive il sistema che stai usando.

React non ha Controller (e questo manda in crisi)

Nel modello MVC:

  • il Model gestisce i dati
  • il View mostra
  • il Controller coordina

In React:

  • non esiste un controller centrale
  • non c’è un punto unico che “decide cosa fare”
  • la UI è una funzione dello stato

Questo è il cuore del modello dichiarativo di React, spiegato chiaramente anche nella documentazione ufficiale quando si introduce il concetto di pensare in React.

Tu non dici:

“quando succede X, fai Y”

Dici:

“se lo stato è X, la UI è Y”

È una differenza sottile, ma devastante se la ignori.

Il danno più comune: componenti-controller mascherati

Quando tratti React come MVC, succede questo:

  • nascono componenti enormi
  • pieni di logica decisionale
  • che orchestrano tutto

Sono controller travestiti da componenti.

Segnali tipici:

  • if annidati ovunque
  • stato che governa mezzo albero
  • side effect gestiti “per far funzionare le cose”
  • difficoltà a testare

Questi componenti diventano colli di bottiglia.
Ogni modifica è rischiosa. Ogni bug è difficile da isolare.

In React il flusso è unidirezionale (e questo cambia tutto)

Un altro punto di rottura con MVC è il flusso dei dati.

In React:

  • i dati scendono
  • le azioni risalgono
  • lo stato guida il rendering

Questo flusso unidirezionale è uno dei principi fondamentali del framework ed è alla base della sua prevedibilità, come spiegato anche nella panoramica MDN su React e sulla gestione dello stato.

MVC invece:

  • prevede più direzioni
  • più punti di coordinamento
  • più accoppiamento implicito

Se cerchi di replicare questo schema in React, stai combattendo il framework.

React non è MVC. È composizione + stato + dichiaratività

Un modello mentale più corretto per React è questo:

  • Componenti → funzioni di rendering
  • Stato → fonte di verità
  • Composizione → modo di costruire UI complesse
  • Eventi → segnali che richiedono cambiamento

Non c’è un controller che coordina tutto.
C’è uno stato che determina cosa esiste.

Quando accetti questo:

  • i componenti diventano più piccoli
  • la logica si distribuisce
  • il codice è più leggibile

Il vero criterio: cosa cambia nel tempo

Se vuoi un principio pratico che funziona meglio di MVC, usa questo:

Se qualcosa cambia nel tempo, è stato.
Se qualcosa reagisce al cambiamento, è rendering.
Se qualcosa descrive come combinare parti, è composizione.

Questo ti evita:

  • controller centrali
  • logica duplicata
  • dipendenze nascoste

Ed è perfettamente allineato con il modo in cui React è stato progettato.

Smettere di cercare MVC è un atto di maturità

React non è MVC.
Non lo diventerà.
E non è un limite.

È un framework pensato per:

  • interfacce dinamiche
  • flussi complessi
  • cambiamento continuo

Continuare a trattarlo come MVC significa:

  • rallentare
  • complicare
  • perdere i benefici reali

Quando smetti di cercare controller e inizi a modellare lo stato e le responsabilità, React diventa finalmente leggibile.

Non perché è semplice.
Ma perché stai usando il modello giusto.

A questo punto il passo naturale è capire come organizzare un progetto React senza cadere in cartelle MVC travestite.


Come strutturare un progetto React

React non chiede una struttura “giusta”. Chiede una struttura coerente

Una verità scomoda:

React non impone un’architettura.

Ed è proprio questo che manda in crisi.

Chi viene da framework opinionati cerca:

  • regole rigide
  • cartelle standard
  • pattern “ufficiali”

React invece ti chiede di fare una cosa più difficile:
👉 pensare in termini di responsabilità e cambiamento nel tempo.

Questo approccio è coerente con quanto spiegato nella documentazione ufficiale quando React parla di composizione e separazione logica, non di layer architetturali fissi (vedi Thinking in React su react.dev).

L’errore più comune: separare per “tipo” invece che per “dominio”

Struttura tipica sbagliata:

components/
hooks/
services/
utils/
pages/

Sembra ordinata.
Ma nasconde un problema serio: la logica di una feature è sparsa ovunque.

Per capire come funziona una singola funzionalità devi:

  • saltare tra cartelle
  • tenere tutto in testa
  • ricostruire il flusso

Questa non è architettura.
È frammentazione.

Chi ragiona in MVC tende a forzare:

  • cartelle controllers
  • cartelle models
  • cartelle views

In React questo spesso produce:

  • frammentazione artificiale
  • passaggi inutili
  • più boilerplate

React non chiede di separare per tipo.
Chiede di separare per responsabilità.

Un criterio migliore: separare per responsabilità funzionale

In React funziona meglio un approccio feature-oriented.

Non separi per “cos’è” un file.
Separi per cosa fa nel dominio dell’app.

Esempio concettuale:

features/
  auth/
    AuthForm.jsx
    useAuth.js
    authService.js
  profile/
    ProfileView.jsx
    useProfile.js
    profileService.js

Qui:

  • tutto ciò che cambia insieme sta insieme
  • la logica è localizzata
  • l’impatto di una modifica è prevedibile

Questo modello riduce:

  • accoppiamento
  • dipendenze nascoste
  • paura del refactor

I componenti non sono “view”: sono unità di comportamento

Altro errore mentale:

“Questo è un componente di presentazione, questo è logica.”

In React questa distinzione è spesso artificiale.

Un componente può:

  • gestire stato
  • reagire a eventi
  • renderizzare UI

E va bene così.

Forzare separazioni premature crea:

  • wrapper inutili
  • prop drilling
  • componenti fantasma

React non ti chiede purezza architetturale.
Ti chiede chiarezza.

Dove mettere lo stato (e dove NO)

Una struttura sana riflette ciò che abbiamo già chiarito sullo stato:

  • stato locale → vicino a chi lo usa
  • stato condiviso → sale solo quando serve
  • stato globale → raro e intenzionale

Se la struttura ti obbliga a:

  • spostare stato “perché la cartella lo impone”
  • centralizzare tutto “per ordine”

stai facendo architettura contro React, non con React.

Attenzione ai “framework mentali importati”

Alcuni segnali che stai importando modelli sbagliati:

  • cartelle MVC in un progetto React
  • servizi che “decidono cosa fare”
  • componenti passivi che aspettano istruzioni
  • logica nascosta fuori dal flusso di rendering

React funziona meglio quando:

  • le decisioni emergono dallo stato
  • il rendering è conseguenza, non comando
  • la struttura segue il dominio, non la tecnologia

Questo è coerente anche con le linee guida MDN su React, dove il focus è sempre su flusso dei dati e responsabilità, non su layer architetturali rigidi.

Una struttura “giusta” è quella che regge il cambiamento

La domanda corretta non è:

“Questa struttura è elegante?”

Ma:

“Quando cambierà questa feature, quante cose dovrò toccare?”

Se la risposta è:

  • “poche, e so dove” → stai facendo bene
  • “dipende, devo cercare” → stai accumulando debito

Una buona architettura React:

  • non impressiona
  • non è accademica
  • sopravvive al cambiamento

Conclusione: l’architettura non è una cartella, è una decisione continua

React non ti dà un’architettura perché:

  • ogni app è diversa
  • ogni dominio ha priorità diverse
  • ogni team evolve

Ma ti dà un modello chiaro:

  • stato come fonte di verità
  • composizione come strumento
  • flusso unidirezionale come garanzia

Se costruisci la struttura seguendo questi principi,
non avrai bisogno di “pattern salvifici”.

Avrai un progetto:

  • leggibile
  • manutenibile
  • onesto

E questo, nel tempo, vale più di qualunque struttura “perfetta”.