La struttura di un progetto React/Redux

L

Intro

La struttura di file e cartelle di una qualunque applicazione gioca un ruolo fondamentale nella buona riuscita di un progetto, un ruolo molto più importante di quanto possiamo pensare o vogliamo ammettere!
Le motivazioni di quanto appena scritto, sono da ricercare in diversi fattori, tra cui anche il modo in cui siamo abituati, da esseri umani, a ragionare.

Invito, però, tutte le persone che leggeranno questo articolo a non prendere la questione troppo sul serio in entrambi i versi, ovvero:

  1. Non andate a rifattorizzare intere applicazioni che avete in produzione se il mio articolo vi avrà convinto.
  2. Non fatene una questione se non vi piace. Alla fin fine, come scriverò, sono scelte personali basate su esperienze che, purtroppo o per fortuna, non tutti hanno sperimentato e per molti di voi che non si sono ritrovati a lavorare magari in applicazioni per mobile (React Native) con quasi 40 schermate, usare una struttura del genere sembrerà come spararsi in un piede.

Chiudo l’introduzione affermando che, seppur il mio lavoro riguarda react e quindi anche questo articolo, sono regole generali che possono riguarda ogni framework e/o libreria utilizzata.

Perché l’ordine conta?

Il motivo per cui l’ordine in cui disponiamo i file è importante, non è un concetto banale da spiegare a chi non ha vissuto il dramma di dover rifattorizzare un’applicazione da 100+ componenti partita con una struttura tutto meno che adeguata; è una di quelle cose che si impara sbagliando.
Un po’ come l’andare in bicicletta o, per fare un paragone che rimane nel nostro campo,  inserire chiavi di servizi esterni come variabili di ambiente della macchina su cui gira il nostro codice e non inserite in chiaro all’interno del codice.

L’idea alla base della questione che voglio portarvi all’attenzione è che l’ordine in cui il nostro codice è disposto influenza il modo in cui il codice stesso viene scritto. E’ un dato di fatto nonostante non ci sia alcuna relazione pratica tra le due cose.

Le due strutture più note

Aldilà delle sottigliezze, come nomenclature di file e cartelle e altri piccoli dettagli, la struttura di un progetto, alla sua radice, ricade sempre in una di queste due tipologie:

  • cartelle per tipologia di file: cartelle per components, per effects (es. redux-saga e/o redux-observables ecc.), per reducers ecc.
  • cartelle per feature (che non rispecchiano necessariamente le routes (o pagine visive) della vostra applicazione) le quali, a loro volta, possono contenere ulteriori cartelle che rispecchiano la stessa struttura (per sotto-feature quindi) o la struttura al primo punto.

Cosa c’è che non va con la prima struttura

Cartelle come “components, reducers, actions…” alla root di un progetto React/Redux, a differenza di Angular, sono una triste realtà molto comune in progetti esemplificativi e non, più o meno piccoli, che è possibile trovare quasi ovunque online.
Questa pratica di ordinare il codice in base a una sua teorica appartenenza a una determinata tipologia non aiuta in nessun caso a facilitare la navigazione nel codice, perché difatti è una metodologia che porta a una nomenclatura completamente estranea al dominio del problema che il nostro codice risolve.
Non sarebbe meglio, quindi, trovare una struttura che si avvicini alla “semantica del nostro codice”? Una struttura di file e cartelle che serva il nostro scopo, ovvero che già a primo impatto ci può dare informazioni qualitative su parti del progetto.
Un’idea ci viene fornita dalla struttura al secondo punto.

La struttura corretta

Sicuramente la struttura al secondo punto si avvicina a quanto abbiamo specificato due righe sopra, ma è sufficiente da sola?
Partiamo dalla regola che ad ogni feature corrisponde 1 solo reducer, 1 solo file di action, 1 solo file di routing e via dicendo e che una feature può non avere alcun componente (esistono feature che riguardano esclusivamente lo state management).
Allo stesso tempo è raro, se non impossibile, che una feature corrisponda ad un unico componente (a meno di componenti giganti, cosa che va anch’essa assolutamente evitata) e quindi per evitare di inserire n. componenti sparsi all’interno di una cartella feature, bisogna fare il seguente ragionamento:

  1. Ho molti componenti per una feature (> 10)?
  2. E’ la mia feature tanto grande da poter essere suddivisa in sotto-feature? Se si, crea una cartella per la sotto-feature all’interno della feature, atrimenti torna al punto 1
  3. Sono i componenti della mia feature utilizzati direttamente anche da altre feature del mio stesso livello o di livelli superiori?

In tutti e tre i casi la struttura finale sarà un unione di struttura per feature e per tipologie di file, con un attenzione alla ricorsività della struttura. Vi sarà più chiaro con un esempio (caso 1):

  • Profile
    – components
    – services
    – store (reducer.js, actions.js, epics.js, actionTypes.js…)
    – index.js (generalmente questo è il container component)

Il caso 2 e 3 invece:

  • Profile
    • – store
    • – index.js
    • – Bio
      • – store
      • – components
      • – index.js
    • – shared
      • – components
      • – services

La cartella shared entra in gioco perché Profile, contenendo sotto-feature, ha dei componenti che usa insieme alla sotto-feature (distinguere tra i componenti condivisi e non al livello di Profile, porterebbe solo a confusione, mettete quindi tutto in shared).
In generale piazzate la cartella shared al livello in cui vi sono più feature e/o sotto-feature che usano i stessi file.

“una feature può non avere alcun componente” WUT??

So che può sembrare strana come cosa, ma nel mentre prendevo spunto a destra e a manca per una buona struttura su un progetto di lavoro, mi sono ritrovato a non riuscire a suddividere correttamente una situazione del genere:

Abbiamo una feature “Profile” a cui l’utente può accedervi solo da loggato (che comprende action-creators, reducer, schermate varie) e che visivamente corrisponde alla classica “pagina profilo” dove l’utente può visualizzare i suoi dati una volta che si è loggato.
Ho il token e altre info quali: mail, nome e altri dati in risposta da un endpoint, per esempio, GET /profile da memorizzare nel reducer di questa feature. Tutte queste sono quindi informazioni relative al profilo di un utente.
Il token però è una info particolare in quanto fa sicuramente parte delle informazioni di un utente ma non proviene, visivamente parlando, dalla feature Profile, bensì da una schermata di Accesso che sarà allora un’altra feature che, attenzione, non conterrà un reducer, ma solo degli action-types e action-creators (epics ecc.).
Questo è dovuto al fatto che stiamo memorizzando il token dov’è giusto che sia memorizzato (nel reducer del profilo utente), ma nella state machine che è redux, l’azione dispatchata che, passatemi il termine, “richiede un aggiornamento del token” può trovarsi in tutt’altra parte.

Questo tipo di pratica qui descritta è autorizzata e incoraggiata nell’uso di redux in quanto aiuta il riuso del codice e permette una migliore suddivisione di quest’ultimo.

È più facile a FARSi che a dirsi

Anche se non vi sembrerà, è cosi. Le parole tendono a intrecciarsi facilmente, specialmente quando magari non si è troppo bravi a spiegare, come me. Ad ogni modo cercherò a breve di fornire un esempio basato su uno dei miei recenti lavori.
Ciò che spero di avervi lasciato con questo articolo, se siete persone che solitamente si impicciano tra i file e le cartelle nei primi progetti o se è tanto tempo che usate una struttura con la quale avete sempre combattuto, è la spinta a farvi usare una struttura più funzionale.
Potrete anche poi finire a usarne una diversa da quella qui descritta con la quale vi troverete meglio e spero che a quel punto tornerete qui a commentare e confrontarla con la mia.

Grazie per l’attenzione 🙂

A proposito di me

Giacomo Cerquone

Appassionato di informatica sin da piccolo, cinefilo accanito, amante di tè e dei posti freddi. Focalizzato in full-stack JavaScript developing, contributor di diversi progetti open source e amante delle tecnologie bleeding edge nell'ambito della programmazione.

Gli articoli più letti

Articoli recenti

Commenti recenti