🕐 Tempo di lettura: 6 minuti

Domani invierò la mia ultima richiesta di messa in produzione su DetectION, il progetto antiriciclaggio che ho seguito per quasi tre anni. I primi mattoni li ho posati all'inizio del 2024 e la prima release del modulo core, il BFF, è di marzo di quell'anno. Oggi quel modulo è alla versione 3.15.1, conta circa 800 classi e 1.300 test – circa 1.100 di unità e 200 di integrazione. Allargando lo sguardo agli altri moduli in produzione si superano le 1.000 classi e si arriva a circa 1.800 test: in tutto circa 41.000 righe di codice applicativo e altre 37.000 di test, al netto di commenti e righe vuote.

Sono di parte, lo ammetto. Però del codice che lascio sono davvero soddisfatto, e prima di voltare pagina mi piaceva raccontare un paio di scelte che, a distanza di tempo, hanno ripagato. Senza entrare troppo nel merito del dominio: parliamo di forma, non di contenuto. Poi si cambia: mi aspetta un nuovo ruolo nella Data Architecture, un mondo più orizzontale e trasversale ai prodotti, con nuovi strumenti, linguaggi e tecnologie da esplorare.

📌 L'idea di fondo: una matrice

Il BFF (Backend For Frontend) è interamente reattivo – Spring WebFlux e R2DBC, dall'endpoint fino al database senza una sola chiamata bloccante. Per tenerlo ordinato l'ho immaginato come una matrice:

Il nome di una classe è la sua coordinata nella matrice: AnomalyController, AnomalyService, AnomalyServiceImpl, AnomalyRepository, AnomalyConverter. Se conosci il dominio e il layer, sai già come si chiama la classe e in che package trovarla. Sembra una banalità, ma su un progetto di queste dimensioni è la differenza tra cercare il codice e sapere dove sta.

📌 Un layer, una responsabilità

Dei layer avevo già accennato nella pillola #10, a proposito della migrazione del modello dati. Qui la regola è semplice: ogni layer fa una cosa sola e conosce solo quello sotto di sé.

richiesta ⇄ controller ⇄ [converter] ⇄ service ⇄ [converter] ⇄ serviceImpl ⇄ repository / client esterni
                ↓
             security

La richiesta scende e la risposta risale, tranne che per il security: il controller lo interroga per primo, per autenticare e autorizzare l'utente, e da lì la catena prosegue verso il service. I converter compaiono due volte: tra serviceImpl e service traducono gli oggetti delle sorgenti esterne nel modello di business; tra service e controller traducono il modello nei DTO esposti al frontend (e viceversa, per gli input). Due le regole da non violare mai: il controller non contiene logica, e il service di business non parla mai direttamente con database o client esterni – passa sempre dal serviceImpl.

Quest'ultimo è forse il layer più sottovalutato: disaccoppia la logica di business dalle sorgenti esterne, decide quale interrogare, logga input e tempi di esecuzione e gestisce le eccezioni quando un sistema non è disponibile. È il punto in cui un errore tecnico diventa un errore applicativo leggibile: al posto di uno stacktrace R2DBC anonimo, un codice d'errore che ti dice quale operazione stava girando e con quale input.

Il tutto è scritto in stile funzionale, adottato in modo sistematico su tutto il progetto: i converter sono Function e BiFunction, e si incastrano direttamente nei .map() e nei vari .zip delle pipeline reattive, senza lambda di contorno. Paradigma funzionale e WebFlux si sposano molto bene, e sono ciò che tiene leggibili anche le pipeline più lunghe.

📌 Una libreria nata strada facendo

Lavorando su DetectION mi sono accorto che molte logiche – log strutturati, gestione delle eccezioni, conversioni, un piccolo engine per eseguire query SQL scritte in file .sql veri e mapparle sugli oggetti – non avevano niente di specifico del prodotto. Servivano a chiunque lavorasse in WebFlux, e sul mercato non c'era una libreria che le mettesse insieme. Così le ho raccolte in Reactive Toolkit, una libreria che ho scritto e fatto crescere insieme al progetto, condivisa tra i moduli. Essendo del tutto slegata dal dominio, potenzialmente potrebbe anche diventare open source. Curiosità: è la stessa libreria che orchestra parte del backend di questo sito.

📌 Sonar: uno strumento, non un giudice

Sonar, in DetectION, lavora su due livelli:

1. nell'IDE, con SonarQube for IDE, come igiene quotidiana: la segnalazione arriva mentre scrivi, quando correggere costa pochi secondi. Nel changelog del progetto la voce «fixed some Sonar issues» compare in modo quasi rituale, sprint dopo sprint – e in uno sprint in particolare ne abbiamo chiusi più di cento in un colpo solo.

2. in pipeline, con SonarQube: ogni build lancia anche l'analisi e aggiorna la dashboard aziendale di code quality, quella dello screenshot qui sotto.

Dashboard SonarQube di Reactive Toolkit e del modulo core

Il risultato, a oggi, sul modulo core: A su Security, Reliability, Maintainability e Security Hotspots Reviewed, zero vulnerabilità e zero issue di affidabilità. Le uniche nove segnalazioni di manutenibilità sono falsi positivi legati all'uso di un builder – escluse quelle, il conto è zero. Coverage al 91,2% e codice duplicato allo 0,5%. Reactive Toolkit, più piccolo (circa 1.700 righe di codice), è tutto in A, con coverage al 94,8% e zero duplicazioni.

Una scelta importante dietro quel 91,2%: abbiamo escluso DTO e modelli dal calcolo della coverage, a meno che non contengano logica. Un getter generato da Lombok non ha niente da testare, e contarlo serve solo a gonfiare la percentuale. Meglio un numero più basso ma reale, che misuri davvero la logica – un tema che avevo già toccato nella pillola #23 sull'illusione della coverage.

Poi ci sono le segnalazioni di sicurezza. È un tema che non è mai passato di moda, ma che oggi torna d'attualità, ora che si parla di AI capaci di individuare e sfruttare le vulnerabilità di un sistema. La stessa AI, però, può lavorare anche dalla parte giusta: Sonar rileva il problema e l'AI, se ben indirizzata, aiuta a risolverlo. Di recente ne abbiamo chiuse due: un percorso di file temporanei costruito in modo troppo fiducioso a partire dall'input, e una cifratura che garantiva la riservatezza ma non l'integrità – un messaggio cifrato poteva essere alterato senza che nessuno se ne accorgesse. Non sono un esperto di cybersecurity, ma è proprio questo il punto: Sonar non si limita a dirti che qualcosa non va, ti aiuta a capire perché.

Sonar non è un giudice da accontentare al termine di uno sprint: è uno strumento molto valido che continua a rileggere il tuo codice. Qualche volta ha torto, ma lo apprezziamo lo stesso – una regola si può anche disattivare in un punto preciso, dove non ha senso, lasciandola attiva altrove, purché sia una decisione consapevole e non una scorciatoia.

📌 L'ultima cosa: la documentazione

Quando sai che lasci un progetto, ti accorgi di quanta conoscenza sta solo nella tua testa. Nelle ultime settimane ho messo nero su bianco sei guide: bootstrap del progetto, architettura a matrice, repository, catalogo degli errori, validatore XBRL e setup per i test di integrazione – per chi può usare Docker e per chi no. Il codice dovrebbe essere il più possibile autoesplicativo, ma non basta: racconta cosa fa, raramente perché è stato scelto di farlo proprio in quel modo.

Grazie a chi ha condiviso con me questi anni. DetectION resta in ottime mani! 🙏

Alla prossima pillola! ☕