La piattaforma, realizzata con tecniche di sviluppo su Web, ha la parte server implementata utilizzando un’architettura a microservizi.
L’architettura a microservizi presuppone un software composto da servizi indipendenti di piccole dimensioni che comunicano tra loro tramite API ben definite e che sono controllati da piccoli team autonomi.
La scelta di utilizzare microservizi è stata determinata dalla possibilità di scalare e sviluppare applicazioni in modo rapido e semplice.
Ciascun microservizio esegue ciascun processo come un servizio ed ogni servizio esegue una sola funzione.
I microservizi comunicano tra di loro attraverso uno strato di messaggistica per il quale è stato scelto un protocollo standard ed uno strumento opensource che lo implementa.
Il software scelto per questa funzione è RabbitMQ, un broker di messaggistica che implementa il protocollo AMQP e si basa sul framework OTP per la gestione dei cluster e dei failover. Le librerie per interfacciarsi a questo broker sono disponibili in svariati linguaggi ed ambiente di programmazione e quindi è adatto alla implementazione di una architettura mista a microservizi.
Ogni servizio utilizzerà, se necessario, un database per la memorizzazione dei dati. Non è indispensabile che tutti i servizi utilizzino lo stesso database e la stessa tecnologia per la memorizzazione dei dati. Essendo la comunicazione tra servizi basata su messaggi e su API, nessun servizio accede ai dati di un altro servizio attraverso una connessione diretta al database.
I database scelti per la memorizzazione dei dati sono: MS SqlServer, Postgre SQL, Redis, MongoDB. I Database elencati coprono ampliamente le casistiche di memorizzazione di dati relazionali/non relazionali/documentali generalmente necessari allo sviluppo moderno delle applicazioni.
La parte client utilizza le moderne tecniche di rendering client side per sfruttare la massimo le caratteristiche dei browsers moderni. Anche il client, come il server, è strutturato in una architettura modulare, denominata micro frontend.
ARCHITETTURA DEI SERVIZI LATO SERVER – BACK END
L’architettura dei server lato servizi quindi la parte back-end è realizzata a microservizi, come descritto nel paragrafo precedente. L’architettura segue un pattern CQRS ed eventi.
Il pattern CQRS
La caratteristica principale di CQRS è la separazione di responsabilità tra le query e i comandi. Nel modello CQRS infatti le operazioni di lettura sono separate dalle operazioni di aggiornamento dati. Il vantaggio di implementare un’architettura in CQRS è l’ottimizzazione delle prestazioni, la scalabilità e la sicurezza. Questo pattern infatti garantisce una elevata flessibilità che permette una migliore evoluzione nel tempo ed impedisce ai comandi di aggiornamento di causare conflitti di merge a livello di dominio.
Criticità dell’approccio tradizionale
Tradizionalmente viene utilizzato lo stesso modello dati per eseguire query su un database e per aggiornarlo. Questo in applicazioni semplici potrebbe essere mantenuto mentre per applicazioni complesse questo approccio potrebbe risultare meno conveniente e poco pratico.
Uno dei fattori che determina i vantaggi di questo approccio di separazione riguarda i diversi carichi di lavoro di lettura e scrittura. Spesso aventi proporzioni differenti, i carichi di lavoro di scrittura e i carichi di lavoro di lettura presentano requisiti di scalabilità e di prestazione diversi tra loro.
A partire da questa considerazione, esistono ulteriori caratteristiche che determinano gli enormi vantaggi della scelta di un patter CQRS. Tra queste la mancata corrispondenza che esiste tra le rappresentazioni in lettura e scrittura dei dati come potrebbe capitare per colonne o proprietà aggiuntive che seppur aggiornate correttamente non sono necessariamente parte di un’operazione. Esiste inoltre la possibilità di generazione di conflitti di dati che possono verificarsi quando le operazioni vengono eseguite in parallelo sullo stesso set. L’approccio tradizionale inoltre potrebbe influenzare negativamente sulle prestazioni a causa sia del carico sull’archivio dati e sul livello di accesso ai dati ma anche a causa dell’eventuale complessità delle query necessarie per recuperare le informazioni. Rimane inoltre di difficile gestione la sicurezza e le autorizzazioni poiché ogni entità è soggetta ad operazioni di lettura e scrittura che potrebbero esporre i dati nel contesto errato.
Alla luce di queste criticità che potrebbero manifestarsi, la realizzazione del software è stata effettuata abbandonando l’approccio tradizionale per seguire invece un modello a microservizi che segue il pattern CQRS.
Struttura del pattern CQRS
Come accennato precedentemente, il pattern CQRS si basa sull’idea di separare le operazioni di scrittura dalle operazioni di lettura. Queste operazioni seguono modelli diversi e quindi comandi differenti che in un caso servono per aggiornare i dati e nell’altro consistono in query per leggere i dati.
Perché questa separazione avvenga in maniera corretta, sono necessarie alcune condizioni tra cui:
- i comandi non devono essere basati su dati bensì su attività;
- i comandi possono essere inseriti in una coda per l’elaborazione asincrona invece che essere elaborati in modo sincrono;
- le query non modificano mai il dataset. Esse restituiscono un oggetto DTO che non incapsula informazioni di dominio.
La separazione tra i modelli di query e i modelli di aggiornamento semplifica la progettazione e l’implementazione anche se genera anche uno svantaggio: il codice CQRS non può essere generato automaticamente da uno schema di database tramite meccanismi di scaffholding come gli strumenti ORM.
Al fine di garantire un isolamento maggiore è possibile separare anche fisicamente i dati di lettura da quelli di scrittura. Così facendo però il database di lettura potrebbe utilizzare il proprio schema di dati ottimizzati per le query. Potrebbero anche essere utilizzati tipi di archivio dati differenti.
Naturalmente, utilizzando database di lettura e scrittura separati sarà necessaria la sincronizzazione che potrebbe essere eseguita facendo in modo che il modello di scrittura pubblichi un evento ogni volta che viene aggiornato il database. In questo caso l’aggiornamento del database e la pubblicazione dell’evento devono essere eseguiti in un’unica transazione.
Nel software sviluppato si è scelto tuttavia di mantenere un database unico.
Framework e strumenti di base
Il Framework utilizzato è Microsoft .Net Core Framework 5.0 e successivi.
Sono utilizzati all’interno del framework strumenti standard per permettere l’utilizzo delle funzionalità dei microservizi e per la gestione e la implementazione dei pattern scelti.
Mediatr
E’ utilizzato per gestire il pattern CQRS in-process e permette la corretta implementazione senza la necessità di avere un gestore degli eventi esterno, ma si occupa internamente di recapitare gli eventi in modo corretto agli handler selezionati.
RabbitMQ
RabbitMQ è utilizzato come dispatcher di messaggi per la implementazione del pattern CQRS con eventi asincroni.
L’attuale implementazione utilizza un helper per semplificare e standardizzare la gestione e la registrazione degli exchange e delle code.
Handler
Grazie a MediatR e RabbitMQ è possibile eseguire un disaccoppiamento completo tra i servizi esposti e la logica di business. Internamente alcuni Handlers si occupano gestire i messaggi recapitati attraverso le code di messaggi, e conseguentemente modificare il modello dati nel caso di Command, oppure preparare il dato per la lettura nel caso di Query.
Database e Map server
I Dati sono memorizzati in Database specifici scelti in base alle caratteristiche del dato stesso. Tutti i dati complessi sono memorizzati in DB Relazionali, più in dettaglio i dati strutturati dei sensori, dei device e delle loro posizioni sono strutturati e memorizzati all’interno di un DB su MS SQL Server. I dati documentali strutturati e gli shapefiles, così come anche le immagini e gli allegati sono memorizzati in una struttura a filesystem, organizzati in modo automatico per dominio di appartenenza e tipo di formato. Un DB Redis è utilizzato inoltre, per indicizzare le informazioni memorizzate nel filesystem ed aggiungere metadati e legami trasversali con altre fonti dati.
ARCHITETTURA DEI SERVIZI LATO CLIENT – FRONT END
L’architettura del software lato front-end è realizzata seguendo la logica dei micro-frontend quindi estendendo i concetti di microservizi anche al mondo front-end.
La tendenza attuale è quella di procedere verso una sempre maggiore ricchezza di funzionalità delle applicazioni broswer, note come app a pagina singola, e spesso sviluppate da un team separato rispetto al back end. Questo rende la manutenzione del monolite front-end sempre più complicata. Per questo motivo si è arrivati alla logica di intendere il sito web o l’app web come una composizione di funzionalità di proprietà di team differenti. Ogni team ha un’area di attività in cui è specializzato. Il team è interfuzionale e sviluppa le sue funzionalità end-to-end, dal database all’interfaccia utente.
Con questa struttura è stata sviluppato il software oggetto del presente documento. Di seguito approfondiamo i moduli base inseriti.