Da macro a micro…tutto!

I microservizi, in teoria

Davide D'Antonio
weBeetle
9 min readMay 19, 2020

--

La tendenza naturale per le nuove società high-tech come Amazon, Netflix, DAZN o Spotify etc. rappresenta la costruzione del loro software utilizzando l’approccio micro, che rappresenta lo scenario ideale: i microservizi ed i microfrontends consentono di ampliare, o modificare, i loro prodotti con un effort, molto spesso, minimo.

Questo è solo il primo di una serie di articoli che intende trattare non solo teoricamente ma anche praticamente, alcuni stili architetturali utilizzati da queste aziende al fine di comprenderne i pro ed, ovviamente, i contro.

In questo primo articolo affronteremo, solo teoricamente, il tema dei microservizi. Quindi iniziamo!

Tutto ha inizio dai monoliti

Capita spesso che le aziende non riescano a strutturare o pianificare il proprio software in anticipo. Invece di pianificare, queste costruiscono software basati sulla crescita organica naturale: pochi componenti software raggruppano i flussi di business per affinità. Non è raro vedere aziende con due grandi componenti software: il sito Web rivolto agli utenti e gli strumenti di amministrazione interna. Questo di solito è nota come architettura software monolitica. C’è da dire che quando si ha a che fare con questa tipologia architetturale si ha anche a che fare, molto spesso, con problemi coordinamento dei team che creano, distribuiscono e manutengono il codice. Infatti i conflitti sul merging del codice sorgente e, di conseguenza, anche regressioni sulle funzionalità impattate risultano sempre più frequenti all’aumentare del codice dell’applicazione. Lo schema più comunemente utilizzato per rappresentare un’applicazione monolitica è il seguente:

Schema di un’applicazione monolita

Partendo dal basso verso l’alto, quello che le aziende mettono in campo il 90% delle volte è il seguente stack:

  • Un database layer (MySQL, MongoDB etc.)
  • Un API layer che si occupa di gestire tutte le invocazioni provenienti dall’esterno, o di gestire le diverse integrazioni con compagnie di terze parti, salvare dati, etc (PHP, Node.js, Java etc.). L’intera logica di business dell’applicazione si trova qui.
  • Una SPA (Single Page Application) o una PWA (Progressive Web Application) che si occuperà di interagire con gli utenti (React, Angular etc.).

Scalabilità

Uno dei problemi comuni in uno schema architetturale, come quello visto in precedenza, è la scalabilità. Se vi è già capitato di lavorare su un software monolitico, sono sicuro che a un certo punto avrete avuto problemi di capacità, insieme alla crescita dell’azienda. Di solito, questi problemi non riguardano tutti i livelli o sottosistemi dell’applicazione. Esiste sempre un sottosistema o un servizio che funziona in modo significativamente più lento rispetto al resto, causando il rallentamento dell’intera applicazione e che non è in grado di far fronte alle richieste degli utenti. In questi casi ci sono due possibili soluzioni:

  1. Scalare verticalmente: in questo caso si aggiungono risorse al server esistente. Ad esempio, aggiungendo più spazio su disco, aggiungendo più RAM, aggiungendo anche una CPU aggiuntiva.
  2. Scalare orizzontalmente: nel caso in cui la prima opzione non risolve il nostro problema, si aggiungono altri server. Quindi l’intera applicazione viene letteralmente duplicata su uno o più server.

Di conseguenza nel momento in cui non riusciamo a risolvere i nostri problemi aggiungendo semplicemente più RAM o CPU, al nostro schema architetturale si aggiunge un load balancer, come illustrato di seguito:

Scalare un monolite orizzontalmente

Pro e contro

Anche se non sembra, questo approccio presenta dei vantaggi:

  • Semplice da sviluppare: l’obiettivo degli attuali strumenti di sviluppo e IDE è supportare lo sviluppo di applicazioni monolitiche.
  • Facile da scalare: potete in qualsiasi momento far girare più istanze del vostro codice su più server, utilizzando un load balancer.
  • Il deploy è facile: basta che aggiornate i server dove gira il vostro codice e siete apposto.

Ora che conosciamo i vantaggi, vediamo quali sono gli svantaggi:

  • Nuovi membri del team: quando il monolita raccoglie diversi anni di esperienze e codice sorgente intimidisce, e credetemi se vi dico non poco, un nuovo membro del team. Addirittura l’entrata di quest’ultimo all’interno del team può rallentare il normale sviluppo dell’applicazione e può impiegare anche diversi mesi.
  • L’IDE inizia a scricchiolare: quando la base di codice del monolita diventa abbastanza grande il vostro editor di sviluppo preferito inizia ad avere qualche problema.
  • Vi lega ad un determinato stack tecnologico: quando non aggiornate il vostro stack tecnologico rischiate di diventare obsoleti nel giro di qualche anno. Un esempio classico è il seguente: immaginate che vengano apportati importanti miglioramenti di performance al linguaggio di programmazione utilizzato nella stesura del codice, ma il vostro monolite è fermo a due versioni precedenti, effettuare un aggiornamento della versione potrebbe richiedere diverso tempo, o forse, potrebbe non essere fattibile.

Microservizi

Che cos’è un microservizio? Un microservizio è un’unità di lavoro autonoma che può eseguire un’attività senza interferire con altre parti del sistema, in modo simile a una posizione di lavoro in un’azienda.

I microservizi sono diventati sempre più popolari. Oggigiorno, praticamente ogni tutti prendono ne in considerazione l’utilizzo al fine di migliorare la qualità e la scalabilità dei sistemi che costruiscono. Ovviamente si parla sempre di sistemi di medie/grandi dimensioni. Non ha senso parlare di microservizi se la vostra applicazione ha poche funzionalità. Chiaro no?

Ricorrere ai microservizi ha una serie di vantaggi che possono essere utilizzati a favore di un team al fine di aiutare a scalare i sistemi di un’azienda.

Caratteristiche

Un microservizio deve avere le seguenti caratteristiche:

  • Deve poter essere sviluppato da un piccolo team. Questa caratteristica è essenziale per un’elevata produttività ed evita spese di comunicazione per i grandi team.
  • Deve avere un basso accoppiamento con gli altri servizi. Questo consente al team di lavorare la maggior parte del tempo sullo sviluppo delle funzionalità del microservizio in modo autonomo, senza essere influenzato dalle modifiche fatte ad altri microservizi e senza influire su altri microservizi.
  • Deve essere facilmente manutenibile e testabile. Questa caratteristica consente di sviluppare e distribuire in modo rapido e frequente.
  • Deve essere rilasciato in modo indipendente. Questo consente ad un team di distribuire il proprio microservizio senza doversi coordinare con altri team.

I servizi comunicano utilizzando protocolli sincroni come HTTP/REST o protocolli asincroni come AMQP. I servizi possono essere sviluppati e distribuiti indipendentemente l’uno dall’altro. Ogni servizio ha il proprio database per essere disaccoppiato (anche se non sempre è così) da altri servizi.

Principi di progettazione

Ci sono tuttavia alcuni principi di progettazione che devono essere presi in considerazione quando si creano microservizi. Non esiste una regola d’oro e, poiché i microservizi sono un concetto recente, a volte non c’è un consenso generale su quali pratiche seguire. In generale, possiamo assumere i seguenti principi di progettazione:

  • I microservizi sono unità di business che modellano i processi aziendali.
  • Sono endpoint intelligenti che contengono la logica di business e comunicano utilizzando protocolli semplici.
  • Le architetture orientate ai microservizi sono decentralizzate per definizione. Questo aiuta a creare software robusto e resiliente.

Com’è strutturato un microservizio?

Il diagramma che segue rappresenta la struttura di un microservizio. Il suo cuore è, ovviamente, la logica di business che è circondata da una serie di adapter che possono comunicare a loro volta con altri microservizi o applicazioni:

Diamo ora uno sguardo ad ogni suo componente

Client API. Si riferisce all’API esposta al pubblico, ossia a tutte le applicazioni che la utilizzano. Ed è la cosa più importante per un microservizio dal punto di vista di chi la utilizza, in quanto definisce le operazioni e gli eventi che questo espone.

Private API. Non sempre un microservizio espone solo un API pubblica. Molto spesso si ha la necessità per un microservizio di definire un metodo specifico ad utilizzo interno. Di conseguenza un microservizio può comunicare internamente con altri microservizi in modo sincrono (HTTP REST), o in modo asincrono (broker di messagi).

Events. Un servizio pubblica spesso eventi. Un servizio pubblica eventi su un canale di messaggi implementato da un broker di messaggi come Apache Kafka, NATS, Redis, ActiveMQ e RabbitMQ.

Business Logic. Il cuore del servizio e la ragione della sua esistenza. Implementa le operazioni dell’API e pubblica eventi. La logica aziendale richiama le operazioni di altri servizi e sottoscrive i loro eventi. Persiste i dati nel database del servizio.

Database. Un servizio di solito ha un database, che archivia i suoi dati e talvolta i dati replicati da altri servizi. Per garantire un accoppiamento basso, è generalmente una cattiva idea che i servizi condividano le tabelle del database. Al contrario, i servizi devono comunicare solo attraverso le loro API.

Ora che abbiamo definito, anche se schematicamente, un microservizio vediamo che aspetto assume lo schema della nostra applicazione, se adottiamo questa metodologia:

Architettura a microservizi

Pro e contro

Esattamente come per i monoliti anche per i microservizi hanno dei pro:

  • Indipendente da framework e linguaggi: ogni microservizio può essere implementato con il linguaggio di programmazione che preferite (PHP, Java, Javascript etc.) ed il vostro framework preferito.
  • Facile da integrare: vista la sua natura risulta facile da integrare o adattarlo per utilizzarlo in altre applicazioni.
  • Manutenzione è più semplice ed economica: è possibile apportare miglioramenti o modifiche un modulo alla volta, lasciando il resto funzionare normalmente.
  • Facilita l’entrata di un nuovo membro nel team: al contrario dell’antagonista monolita, l’entrata di un nuovo membro nel team di sviluppo è facilitata.
  • Scali un servizio alla volta: anziché scalare l’intera applicazione scali un microservizio alla volta.
  • Velocità di rilascio: consentendo di abbreviare i cicli di sviluppo, un’architettura basata su microservizi supporta deployment e aggiornamenti più agili.

Ovviamente, come qualsiasi cosa, l’approccio a microservizi ha i suoi contro:

  • Non è adatto a team di piccole dimensioni: se non si hanno le risorse necessarie adottare questo pattern strutturale è una via verso il suicidio. Immaginate semplicemente un team di due sviluppatori che deve gestire un’applicazione composta da venti microservizi.
  • Scrivere i test è complicato: scrivere i test per un solo microservizio è facile ma farlo per l’intero ecosistema richiede molto effort.
  • Competenze: l’approccio a microservizi richiede sviluppatori esperti con un livello di competenza molto elevato.

API Gateway

Quando si sceglie di creare un’applicazione con l’approccio a microservizi, bisogna anche decidere come interagiranno i client dell’applicazione con loro. Con un’applicazione monolitica esiste un solo set di endpoint (eventualmente bilanciati da un load balancer). In un’architettura orientata ai microservizi, tuttavia, ogni microservizio espone un’API al pubblico, di conseguenza un client effettua richieste direttamente al microservizio che gli interessa. Ogni microservizio ha il suo endpoint come per esempio https://servizio.api.dominio.com. L’immagine mostrata nel precedente paragrafo mostra esattamente questo approccio. Ma…quando i microservizi diventano 50? Cosa accade al team che si occupa dello sviluppo del, o dei, client?

Di solito un approccio migliore consiste nell’utilizzare ciò che è noto come API gateway. Un API gateway è un server che rappresenta il singolo punto di ingresso nel sistema. L’API gateway incapsula l’architettura di sistema interna e fornisce un’API su misura per ciascun client. Potrebbe avere altre responsabilità come autenticazione, monitoraggio, bilanciamento del carico, memorizzazione nella cache, gestione delle richieste e gestione delle risposte statiche.

Il diagramma seguente mostra come un’API gateway si adatta all’architettura descritta nel paragrafo precedente:

Architettura a microservizi con API Gateway

Come vedete ora tutte le richieste dei client vanno verso l’API gateway che, sua volta si occupa di indirizzarle verso il microservizio appropriato. L’API gateway gestirà spesso una richiesta richiamando più microservizi e aggregando i risultati.

Pro e contro

L’utilizzo di un gateway API presenta sia vantaggi che svantaggi. Un grande vantaggio dell’utilizzo di un gateway API è che incapsula la struttura interna dell’applicazione. Invece di dover invocare servizi specifici, i client parlano semplicemente con il gateway. L’API gateway fornisce ad ogni tipo di client un’API specifica.

L’API Gateway presenta anche alcuni svantaggi. È un altro componente altamente disponibile che deve essere sviluppato, distribuito e gestito. Esiste anche il rischio che diventi un collo di bottiglia nello sviluppo. Gli sviluppatori devono aggiornarlo per esporre gli endpoint di ciascun microservizio. È importante che il processo di aggiornamento dell’API Gateway sia il più leggero possibile. Altrimenti, gli sviluppatori saranno costretti ad attendere in fila per aggiornare il gateway. Nonostante questi inconvenienti, tuttavia, per la maggior parte delle applicazioni del mondo reale ha senso utilizzare un’API gateway.

Concludendo

In questo primo articolo abbiamo fatto una brevissima introduzione ai microservizi. Nel prossimo articolo vedremo come implementare un’applicazione di ticketing utilizzando i microservizi e Node.js. Per il momento…Happy Reading!

Bibliografia

--

--

Davide D'Antonio
weBeetle

👨‍🎓Degree in computer science 💑 Married with Milena 🤓 Huge Nerd! 💻 Code lover 👨🏻‍💻 Fullstack developer