Oggi le architetture dei sistemi più complessi sono parzialmente o totalmente basate sull'approccio a microservizi. Molto spesso però ci si dimentica che tali sistemi sono molto più complessi rispetto a quelli "tradizionali" ed una maggiore complessità porta a maggiore impegno e manutenzione da parte del team di sviluppo. Per migliorare l'esperienza utente il carico di lavoro per ogni singolo membro del team può infatti aumentare esponenzialmente.
Ma cosa sono esattamente i microservizi? Il tradizionale approccio alla creazione delle applicazioni, definito "monolitico", prevede che tutti i componenti delle App di cui è possibile eseguire il deployment siano contenuti all'interno della stessa. Questo comporta però degli svantaggi: maggiori sono le dimensioni dell'applicazione e più difficile sarà risolvere rapidamente nuovi problemi e aggiungere nuove features.
Granularità
Un approccio alla creazione delle applicazioni basato sui microservizi mira a semplificare la risoluzione di tali problemi. I microservizi sono sia un'architettura sia un approccio alla scrittura di software.
Con i microservizi le applicazioni vengono scisse in componenti più piccoli ed indipendenti. Separati l'uno dall'altro questi componenti interagiscono solo per completare l'attività impostata dallo sviluppatore e ciascun componente (o processo) rappresenta un microservizio. Tale approccio promuove quindi la granularità e consente di condividere processi simili tra più applicazioni.
L'obiettivo di un microservizio è fornire un software di qualità elevata in tempi più rapidi. Ci sono però altri aspetti da prendere in considerazione. Suddividere le App in microservizi non è sufficiente. È infatti necessario gestirli, organizzarne i processi e occuparsi dei dati che questi creano e modificano. Tali fattori possono aumentare la pressione e i lavoro svolto dal singolo sviluppatore.
Connessioni nascoste
Per assicurare che il proprio progetto basato sui microservizi goda di buona salute è bene trovare la giusta condivisione delle responsabilità. Definire dei confini tra i componenti è essenziale, cosi si riduce al minimo la possibilità che si vengano a creare delle "connessioni nascoste" tra diversi i servizi. In realtà, è abbastanza difficile definire le varie responsabilità in modo netto. Infatti quando un componente è dedicato a un determinato processo, sono presenti spesso interconnessi multiple con altri processi.
L'errore più comune per un team di sviluppo è ignorare una divisione netta tra i vari componenti del microservizio, pensando che in futuro si possa operare per riparare a qualche svista, ma in realtà più tempo si aspetta per separare nettamente i processi più questi in futuro saranno legati ad altri processi e dipendenze.
Tali connessioni non previste privano ovviamente il microservizio della sua flessibilità e spesso creano molti disagi sia all'utente che allo sviluppatore.
Interoperabilità
Quando si introduce una nuova funzionalità in un servizio o si implementa una nuova versione, è necessario assicurarsi di non danneggiare la sua interoperabilità con gli altri servizi. Nei casi peggiori infatti ci si può ridurre a dover rilasciare in blocco una nuova versione dell'applicazione rinunciando quindi alla modularità del microservizio.
Discorso simile vale per le API, che dovrebbero essere descritte nel dettaglio e standardizzate sin dall'inizio. Mantenendole neutrali e riutilizzabili, questa impostazione semplifica enormemente il monitoraggio e la gestione dei protocolli di sicurezza, consentendo anche la rapida introduzione di nuovi componenti (grazie alla standardizzazione) oltre ad ottimizzare l'integrazione futura con componenti di terze parti.
Codice condiviso
Per quanto riguarda le componenti condivise con altri progetti è un bene sfruttarle nei giusti limiti e coordinandosi sempre con i vari team di sviluppo. Il codice condiviso richiede infatti un trattamento speciale in fase di aggiornamento. Ogni volta che si decide che il proprio servizio necessità di un update della libreria comune bisogna informare gli altri gruppi dei microservizi dipendenti e assicurarti che i rispettivi microservizi siano pronti per le modifiche.
Questo perché spesso una modifica non notificata può rompere il funzionamento del microservizio sopratutto se i vari componenti condividono tra di loro codice e librerie di base.
Protocolli di blocco
Altro fattore da prendere in considerazione per evitare il fallimento del proprio sistema di microservizi è garantire una forte coerenza, questo in un sistema distribuito implica spesso l'uso di protocolli di blocco che aumentano in modo significativo i tempi di risposta e creano ulteriori dipendenze, il che complica il futuro ridimensionamento e carica la rete.
Il problema si pone molto spesso durante le transazioni lunghe e distribuite di carattere critico, come ordini, operazioni di prenotazione, ecc. In questo caso, il sistema può ad esempio richiedere denaro all'utente, ma non riceve mai l'ordine o la camera d'albergo prenotata. Bisogna quindi prendere in considerazione l'introduzione di una serie di sistemi di rollback o altri meccanismi di compensazione che magari annullino l'intera operazione in caso di un parziale errore nel salvataggio.
Monitoraggio
Un corretto monitoraggio è essenziale quando si ha a che fare con un sistema basato su microservizi che ha molti componenti interoperabili. È quindi necessario verificare quanto bene stanno funzionando tali componenti. In certi casi si può ad esempio incappare in una situazione in cui l'intero sistema va a rilento e non si riesce a capire perché ha performance così limitate.
Per evitare questo si possono usare framework come Zuul o Hystrix, che non solo controllano se i servizi sono attivi ma riportano anche le loro prestazioni e lo stato corrente, isolano il servizio in crash dandogli il tempo per recuperare il reinstradamento delle query. Forniscono inoltre statistiche preziose sulla produzione del sistema e possono segnalare tempestivamente la necessità di miglioramenti.
Progettare un sistema che è completamente pronto a superare i vari casi di emergenza è qualcosa che implica l'instaurazione di una routine quotidiana. Tenere performante ed operativo un sistema di microservizi può essere quindi più impegnativo di uno tradizionale. Gli errori nei progetti di microservizi sono più facili da realizzare a causa della maggiore complessità di tali sistemi.
Dunque bisogna sempre prendersi cura delle transazioni lunghe e distribuite, dividere chiaramente le responsabilità dei microservizi, evitare una "spericolata" condivisione di codice e librerie, standardizza le API ed introdurre un sistema di monitoraggio approfondito.