Cos’è la Clean Architecture?

Il termine Clean Architecture deriva dall’omonimo libro di Robert C. Martin. Questo libro è uno di quelli che ti consiglio assolutamente di leggere se sei uno sviluppatore o se vuoi diventarlo, non deve assolutamente mancare nella biblioteca di un dev che si rispetti!

Nel libro viene illustrato in modo estensivo e molto chiaro il concetto di Clean Architecture, ma se dovessi sintetizzare in una frase sola, direi che possiamo pensare alla Clean Architecture come un insieme di linee guida per progettare l’architettura di un software. OK, forse come definizione è un po’ generica, ma nel resto dell’articolo cerco di andare un po’ più in profondità.

Prima di tutto partiamo dal “perché”: perché mi serve pensare alla architettura e perché dovrei interessarmi alla Clean Architecture?

Perché serve una architettura?

Se sei uno sviluppatore software con un po’ di esperienza nello sviluppo di qualsiasi tipo di applicativo (che sia backend, app mobile app, web o altro), ti sarai scontrato sicuramente con il problema di progettare un’architettura per la tua applicazione. Probabilmente in qualche caso ti è capitato anche di partire senza un’architettura ben definita, per poi accorgerti che il progetto diventava veramente difficile da gestire man mano che venivano aggiunte nuove funzionalità; detto in gergo un po’ più tecnico, hai scoperto che l’architettura non è scalabile.

Progettare un’architettura è fondamentale nel processo di sviluppo del software, perché permette di gettare le fondamenta di quello che poi dovrà essere l’applicativo completo; se parto con delle fondamenta deboli, il software che ho costruito sopra è pronto a crollare da un momento all’altro.

Progettare l’architettura non è necessariamente un processo complesso. Ad esempio se voglio realizzare un POC (proof of concept) o un prototipo, un buon compromesso può essere l’utilizzo di una architettura molto semplice. Se invece il mio software è destinato a crescere (pensiamo ad esempio ad un servizio di backend, un’applicazione desktop o un’applicazione mobile di media o grande complessità), allora può essere utile progettare una architettura più solida, che possa essere:

  • scalabile (cioè mi permette di aggiungere agevolmente nuove funzionalità e gestire la complessità crescente senza dover modificare l’achitettura stessa);
  • facile da modificare, permettendomi quindi di apportare modifiche alle funzionalità già implementate modificando il minor numero possibile di sorgenti;
  • testabile (su questo torneremo più avanti).

… e perché dovrei pensare ad una Clean Architecture?

Esattamente per i 3 punti che ti ho appena esposto. Come detto prima non tutte le architetture ti permettono di avere un software scalabile, facile da modificare e testabile; è necessario che l’architettura rispetti determinati requisiti (che vedremo in dettaglio in seguito).

Precisando ulteriormente, non sto parlando di una architettura ben definita e specifica da usare in tutti i casi, ma piuttosto di una serie di principi ben definiti da utilizzare per la progettazione dell’architettura.

I principi della Clean Architecture

Possiamo riassumere i principi della Clean Architecture nei seguenti punti:

  • Indipendente dal framework
  • Indipendente dalla UI
  • Indipendente dal database
  • Testabile

Indipendenza dal framework

L’architettura deve essere indipendente da qualsiasi framework o libreria. Facendo un esempio più concreto, supponiamo che tu debba progettare l’architettura di una applicazione Android nativa (sto già restringendo il campo per semplicità); sicuramente le prime domande che ti farai saranno simili a queste:

  • quale libreria userò per fare le chiamate alle API Rest?
  • quale framework voglio usare per la dependency injection?
  • i dati locali li salverò nelle SharedPreferences oppure in un database?

Ti sembrerà assurdo, ma queste domande non dovresti nemmeno portele in fase di studio della architettura. Devi essere in grado di astrarti da questi dettagli, altrimenti potrebbero rivelarsi un vincolo troppo stretto che ti impediscono di apportare modifiche facilmente.

Indipendenza dalla UI

L’interfaccia utente è una delle componenti più soggette a modifiche nel ciclo di vita dello sviluppo di un software, soprattutto se si parla di interfacce grafiche. Pensa se fosse necessario, ad esempio, modificare le classi che gestiscono le chiamate alle API ogni volta che ti chiedono di aggiornare il layout di una vista, oppure se dovessi modificare le classi che implementano le regole di business se la richiesta riguarda solo la modifica del layout degli elementi di una lista. Quello che una buona architettura deve consentirci è di separare le componenti in modo che siano facilmente modificabili senza interferire le une con le altre.

Indipendenza dal database

Qui andiamo sul pesante. Quante volte è capitato di partire proprio dalla definizione della base di dati? Dobbiamo usare SQL o NoSQL? MySQL, Postgres o SQL Server? Firebase? Mongo? Quante tabelle mi servono? E via di seguito, insistendo sul fatto che questa debba essere la base dell’architettura. Ma non è l’approccio corretto. Questo non significa che il database non sia importante (lo è eccome!), ma piuttosto che l’architettura deve consentirti di essere indipendente dall’implementazione specifica del database, perché, se ci pensi, le regole di business di un software sono indipendenti da come decido di persistere i dati. Tra l’altro questo approccio mi da la possibilità di sviluppare anche senza un database (che magari è in carico ad un collega), utilizzando una classe che fornisce dati mock; sostituirò poi questa classe con l’implementazione reale del database quando questa sarà pronta (su questo punto tornerò più in profondità in seguito).

Testabile

Le diverse componenti del mio software dovrebbero essere testabili in modo indipendente le une dalle altre. Ad esempio devo essere in grado di testare le regole di business senza bisogno di accedere all’interfaccia grafica, al database o alle API di backend. Quindi la mia architettura deve favorire gli unit test. Gli integration test (i test che mi permettono di testare l’integrazione di più funzionalità dell’applicativo) o test end to end (i test dell’applicativo nella sua interezza) continuano ad essere importanti nel ciclo di vita del software, ma testare le componenti singole mi permette di validare fin da subito il comportamento del singolo metodo o della singola classe, isolando facilmente eventuali bug e accelerando così il ciclio di sviluppo del software.

Rimandare le decisioni critiche

I principi elencanti qui sopra hanno come conseguenza un altro caposaldo della Clean Architecture:

Ogni buona architettura ti permette di rimandare le decisioni critiche

Questo è uno degli aspetti che mi ha coplito di più la prima volta che studiato la Clean Architecture, e a mio avviso è uno degli aspetti più potenti.

Ma cosa significa “rimandare le dicisioni critiche”?

Significa aver la possibilità, in fase di progettazione così come in fase di sviluppo, di non preoccuparsi di “dettagli implementativi” che ancora non conosciamo.

Provo a fare un piccolo esempio (affronterò un caso pratico completo nei prossimi articoli). Supponi di dover progettare una app di ricerca ristoranti; un aspetto fondamentale è sapere quale sarà la sorgente dei dati, ovvero da dove andrai a prendere i ristoranti da mostrare all’utente. E’ verosimile che i dati saranno forniti da un backend attraverso una API REST, ma al momento non è disponibile ancora questa informazione; come puoi procedere? Potresti creare una interfaccia Repository in un modo simile al seguente:

interface Repository {
 
  List<Restaurant> search(String query)
 
}

Questa interfaccia rappresenterà il “punto di accesso” ai dati, senza bisogno di sapere quale dovrà essere l’implementazione concreta. In questo modo stai “rimandando” diverse decisioni, come ad esempio:

  • quale libreria dovrò utilizzare per le chiamate REST?
  • dovrò gestire la sincronizzazione dei dati con un DB locale?
  • quale database locale devo utilizzare?

Rimandando queste dicisioni puoi continuare a progettare l’architettura in base alle regole di business e gli altri requisiti che sono già noti.

Integrazione con altri pattern

Una domanda che mi sento porre spesso è: come posso integrare la Clean Architecture nel mio progetto che segue già il pattern MVP / MVVM / MVI ecc…?

E’ fondamentale capire che la Clean Architecture non è un pattern di programmazione alternativo a MVP, MVVM ecc. Questi pattern infatti hanno lo scopo di definire una architettura per lo strato di “presentation” del software, mentre la Clean Architecture ha lo scopo di definire una chiara separazione degli strati del software e la comunicazione tra questi strati.

Conclusione

In questo articolo ho provato a darti una idea di base di cosa si intende per Clean Architecture e di come questo sia un concetto che può essere applicato a qualsiasi tipo di software, indipendentemente dal linguaggio di programmazione, dai pattern e dai framework che si vogliono utilizzare.

Nel prossimo articolo andrò più in profondità su questi aspetti, chiarendo cosa si intende per “strati” (o livelli o layer) del software e la comunicazione tra loro.

Riferimenti