• Non ci sono risultati.

Integrazione di un'applicazione web orientata ai microservizi in ambito bancario = Integration of a microservice-oriented web application in the banking sector

N/A
N/A
Protected

Academic year: 2023

Condividi "Integrazione di un'applicazione web orientata ai microservizi in ambito bancario = Integration of a microservice-oriented web application in the banking sector"

Copied!
104
0
0

Testo completo

(1)

Corso di Laurea in Ingegneria Informatica

Tesi di Laurea

Integrazione di un'applicazione web orientata ai microservizi

in ambito bancario

Relatore

Prof. Giovanni Malnati

APRILE 2022

Candidata Elisa Ratto

(2)
(3)

Alla mia famiglia e a Stefano, per l’amore ed il sostegno costante, per aver sempre creduto in me.

(4)

I

Obiettivo della tesi è l’analisi del progetto assegnatomi come oggetto di tirocinio, quale lo sviluppo di funzionalità integrative all’interno di un applicazione web

orientata ai microservizi in ambito bancario, affiancato dall’aggiornamento di alcune già esistenti.

Il progetto nasce nell’ambito di un’iniziativa di business che ha l’obiettivo di

implementare una totale revisione dell’applicazione già esistente al fine di apportare un miglioramento in termini di efficienza e garantire una user experience più

intuitiva ed immediata.

L’applicativo viene analizzato relativamente agli aspetti architetturali che ne sono alla base, alle scelte implementative effettuate ed alle tecnologie utilizzate, ponendo maggiore attenzione sullo sviluppo del backend dell’applicativo, essendo ciò di cui mi sono occupata nel corso del tirocinio.

All’interno della tesi è presente un’analisi delle principali tipolologie architetturali esistenti: monolitica, service-oriented ed a microservizi, con un particolare focus su quest’ultima.

Segue l’analisi delle scelte architetturali applicate al progetto ed una descrizione del processo di sviluppo e rilascio di un microservizio.

Infine, la conlusione della tesi presenta un’analisi dei risultati ottenuti e le osservazioni sui possibili sviluppi futuri.

(5)

II

Ringrazio il professor Giovanni Malnati, per la disponibilità e per avermi seguito in questo percorso di tesi.

Ringrazio il team con cui ho lavorato nel corso del tirocinio, e con cui ho ancora l’opportunità di lavorare, per aver reso il mio ingresso nel mondo lavorativo un’esperienza positiva, per la disponibilità ed il supporto ricevuto.

Un ringraziamento particolare va al team di backend, Simone Velonà, Giuseppe Giacalone ed Agostino Mazza Pallone, per la gentilezza e la comprensione ricevuta, per il prezioso aiuto e per aver condiviso con me le vostre conoscenze permettendomi di imparare tanto in questi mesi.

(6)

III

Indice

Definizione dei termini, acronimi e abbreviazioni ... VI

1. Introduzione ... 1

1.1Stage ... 1

1.2 Obiettivo della tesi ... 2

2. Introduzione al progetto ... 3

2.1 Contesto e finalità ... 3

2.2 Driver progettuali ... 5

3. Tecnologie Utilizzate... 6

3.1 Java ... 6

3.2 Intellij ... 7

3.3 Spring Boot ... 7

3.4 Hibernate ... 8

3.5 Maven ... 8

3.6 MySQL ... 9

3.7 GitLab ... 9

3.8 Junit ... 10

3.9 SonarLint ... 10

3.10 SonarQube ... 10

3.11 Swagger ... 11

3.12 Postman ... 11

3.13 Artifactory ... 12

(7)

4. Architetture applicative ... 15

4.1 Architettura monolitica ... 15

4.2 Service-Oriented Architecture (SOA) ... 17

2.3 Architetture a Microservizi ... 19

4.4 Confronto ... 21

5. Microservizi... 23

5.1 Cosa differenzia i microservizi dalle altre architetture? ... 23

5.2 Benefici delle architetture a microservizi ... 25

5.3 Integrazione ... 27

5.4 Database ... 27

5.5 Comunicazione tra microservizi ... 27

5.6 User Interface ... 29

5.6.1 Aggregazione dei componenti ... 29

5.6.2 Backends for Frontends ... 30

5.7 Continuous Integration e Continuous Delivery ... 30

5.8 Service-to-Host-Mapping ... 33

6. Architettura generale ... 35

6.1 Principi e requisiti ... 35

6.2 Architettura logica generale ...38

6.2.1 Presentation Layer ... 39

6.2.2 Channel Specialization e Application Layer ... 40

6.2.3 System & Data Layer ... 42

6.2.4 Common Layer ... 43

6.3 Scelte architetturali ... 46

6.3.1 Aumento resilienza ... 46

6.3.2 Miglioramento performance ... 48

6.3.3 Semplificazione gestione operativa ... 48

6.4 Vincoli ... 49

6.4.1 Vincolo 1: Utilizzo Cloud per business critical... 49

6.4.2 Vincolo 2: Costi running e coesistenza ... 50

6.5 Componenti logiche dell’applicazione ... 51

7. Sviluppo di un microservizio backend ... 54

(8)

7.1.2 Scrittura delle API ... 55

7.1.3 Build API ... 69

7.1.4 Utilizzo delle API ... 70

7.2 Integrazione Frontend e Backend ... 72

7.2.1 Integrazione Sincrona ... 72

7.2.2 Integrazione Asincrona ... 74

7.3 Sviluppo Backend: BFF e DMN... 75

7.3.1 Feign ... 76

7.3.2 Proxy (passthrough)... 76

8. Rilascio di un microservizio backend ... 78

8.1 Ambienti ... 78

8.1.1 Sviluppo ... 79

8.1.2 Quality Assurance ... 80

8.1.3 Produzione ... 81

8.1.4 Transizioni tra gli ambienti...82

8.2 Software lifecycle e Release Management ...83

8.3 Fasi del ciclo di vita del software ...83

8.3.1 Realizzazione ... 85

8.3.2 Integrazione ... 85

8.3.3 System Test ... 86

8.3.4 User Acceptance Test ... 86

8.3.5 Pre Produzione ... 87

8.3.6 Produzione ... 88

9. Conclusioni ... 89

Bibliografia e Fonti ... 92

(9)

VI

Definizione dei termini, acronimi e abbreviazioni

Termine, acronimo o abbreviazione

Definizione

BFF Microservizio Backend for frontend

DMN Microservizio di dominio

OAM Oracle Access Management (Identity Management)

JWT JSON Web Token

CI Configuration Item

SPA Single Page Application

ETL Extract Transform Load

CDC Change Data Capture

RDV Rete di Vendita

UX User Experience

API Application Programming Interface

(10)

VII abbreviazione

CMS Content Management System

RFC Request for Change

DBMS Database Management System SOAP Simple Object Access Protocol

UAT User Acceptance Test

OHS Oracle HTTP Server

EJB Enterprise JavaBeans

WFEM Wave/finite element method

CRUD Create, read, update, e delete

Cornerstone Piattaforma per la gestione della didattica e l'erogazione dei corsi per Family Banker e dipendenti

(11)
(12)

1

Capitolo 1

Introduzione

Questo capitolo è un’introduzione al progetto di stage e alla tesi: contiene una breve descrizione del progetto seguito durante l’esperienza di tirocinio, che verrà trattato più nel dettaglio nel corso del documento, e gli obiettivi della tesi.

1.1 Stage

Il progetto assegnatomi durante il tirocinio, svolto presso l’azienda Deloitte, è un lavoro di integrazione relativo ad una web application orientata ai microservizi in ambito bancario, in particolare sono stata assegnata al team che si occupa dello sviluppo del backend dell’applicativo. L’obiettivo di questo progetto è l’introduzione di nuove funzionalità e widget all’interno dell’applicazione e l’aggiornamento di alcune funzionalità già presenti al fine di migliorarne l’efficienza e garantire una user experience più intuitiva ed immediata.

Lo stage mi ha permesso di conoscere nuove tecnologie nell’ambito dello sviluppo di applicativi web, approfondire le mie conoscenze ed osservare il lato organizzativo e gestionale del progetto.

(13)

2

1.2 Obiettivo della tesi

La tesi ha come obiettivo l’analisi dell’architettura dell’applicazione web, delle scelte implementative e delle tecnologie utilizzate nello sviluppo.

Il documento si sviluppa nel seguente modo: partendo dall’introduzione del progetto si pone il focus sul contesto in cui si posiziona e sulle finalità ad esso legate; segue l’enumerazione delle tecnologie utilizzate e l’analisi della struttura dell’applicativo, quindi delle scelte architetturali e le motivazioni legate ad esse; nel corso del

documento verranno analizzati i principali modelli architetturali proposti nell’ambito degli applicativi web: quindi, le architetture monolitiche, SOA ed a microservizi, con un particolare focus su queste ultime in quanto il progetto si colloca all’interno di questa categoria; l’analisi dei modelli architetturali si conclude con un confronto tra gli stessi al fine di evidenziarne le differenze ed osservare i vantaggi e gli svantaggi di ognuno.

All’interno dei capitoli inerenti al progetto, vengono analizzate le tecnologie utilizzate per lo sviluppo applicativo, prevalentemente per quanto concerne il lato backend, cercando di comprenderne le potenzialità e le funzionalità offerte, sia relativamente all’ambiente di sviluppo, sia alle fasi di rilascio e testing dei microservizi sulle piattaforme disponibili.

In conlcusione, un’analisi dei risultati ottenuti e degli obiettivi raggiunti.

(14)

3

Capitolo 2

Introduzione al progetto

L’obiettivo di questo capitolo è definire il contesto in cui si sviluppa il progetto e le finalità dell’applicativo.

2.1 Contesto e finalità

L’applicativo trattato si pone in ambito bancario, il progetto ha il fine di integrare il modello AS-IS dell’applicativo bancario già esistente, relativamente alla sezione dedicata al family banker, facendo riferimento al modello TO-BE richiesto che è finalizzato a rendere l’applicativo più efficiente, aggiungendo ed ottimizzandone le funzionalità e migliorando la user experience dell’utente finale.

Il family banker è un consulente bancario che ha un contatto diretto con il cliente, cioè una figura interna alla banca associata al cliente per soddisfarne le richieste ed offrire i servizi bancari in modo completo.

Il progetto, propostomi come oggetto di stage, nasce nell’ambito di un’iniziativa di business che ha l’obiettivo di implementare una totale revisione dell’applicazione già esistente a supporto delle attività commerciali delle Rete di Vendita della Banca.

Propedeuticamente alle attività di progettazione e sviluppo della customer experiene complessiva, è stato attivato il progetto di reingegnerizzazione dell’architettura esecutiva necessaria per realizzare le capabilities richieste dal design della UX e migliorare l’architettura della banca, a tal fine da un sistema SOA ci si è spostati verso un’architettura a microservizi.

(15)

4

L’immagine seguente illustra il contesto di riferimento dell’applicazione: il nuovo ambiente operativo è stato basato su componenti sviluppate nell’architettura della banca ed integrato con applicazioni del Portale di Rete già esistenti al fine di traguardare i due macro-obiettivi dell’iniziativa che si possono riassumere nei seguenti:

- Ottimizzazione della struttura: miglioramenti riguardanti la struttura di navigazione, atta a rispondere all’esigenza da parte del family banker di avere un accesso più immediato agli strumenti che maggiormente utilizza;

- Ottimizzazione dei contenuti: miglioramenti riguardanti la struttura delle viste da parte del family banker di modo che lo stesso potrà fruire di viste aggregate di informazioni e grafici che gli permetteranno con più facilità di attuare le proprie strategie commerciali.

Figure 1 - Diagramma di contest

(16)

5

2.2 Driver progettuali

Il lavoro progettuale è stato guidato dai seguenti driver: resilienza, performance, gestione operativa e adeguatezza tecnologica. Si riportano di seguito, per ognuno, gli obiettivi.

- Nell’ambito della resilienza e delle performance:

- L’introduzione di un design pattern di circuit break per aumentare la resilienza, itercettando i fallimenti infrastrutturali o applicativi il prima possibile, cioè si vuole introdurre un metodo di gestione delle chiamate verso altri server che limiti le attese: quindi, nel caso in cui un server non sia disponibile o abbia un’alta latenza, dopo il fallimento di un certo numero di chiamate, le successive verranno immediatamente tornate come failed e, quindi, bloccate per un periodo di tempo, finito il quale il server verrà nuovamente testato;

- Implementazione della gestione degli errori ottimizzata sul frontend che permette di evitare crash della pagina in presenza di errori su un singolo widget.

- Relativamente alle performance:

- Ottimizzazione dell’architettura di front-end, mediante il

disaccoppiamento delle componenti applicative dal portale;

- La riduzione del peso dei pacchetti del front-end, grazie all’estrazione e all’accoppiamento a fattor comune delle dipendenze comuni;

- L’introduzione di un nuovo sistema di cache lato backend.

- Per quanto riguarda la gestione operativa:

- La riduzione del numero dei layer di componenti di back-end sviluppati in tecnologie disomogenee.

- Infine per l’adeguatezza tecnologica:

- L’introduzione del concetto di Event Bus per supportare chiamate asincrone ed introdurre pattern di integrazione reattivi con le

applicazioni di front-end;

- Il supporto ad applicativi domain-driven mediante l’introduzione di event bus e microservizi;

- La predisposizione di un’architettura cloud ready utilizzando framework di sviluppo agnostici rispetto all’ambiente di deploy (Vuejs fruibili senza Portale Liferay, applicazioni Springboot senza Application Server WebLogic).

(17)

6

Capitolo 3

Tecnologie Utilizzate

L’obiettivo di questo capitolo è elencare e descrivere brevemente i software e le

tecnologie utilizzate nel corso del progetto per supportare lo sviluppo dell’applicativo.

3.1 Java

Java è un linguaggio di programmazione object-oriented basato su classi, oggetti e metodi. Nei linguaggi orientati agli oggetti i dati sono rappresentati come oggetti e le operazioni come metodi che operano su di essi.

Java nasce con l’obiettivo di creare un linguaggio di programmazione performante e sicuro.

Lo slogan di Java è: “write once, run everywhere”, noto con l’acronimo WORA.

Infatti il codice è utilizzabile su più piattaforme senza la necessità di eseguire una ricompilazione per ognuna: è sufficiente eseguirlo una volta su una piattaforma per ottenere una versione che sia eseguibile anche sulle altre.

Componenti fondamentali di Java sono: la Java Virtual Machine (JVM), il Java Runtime Environment (JRE) ed il Java Development Kit (JDK).

(18)

7

Figura 1 - Logo di Java

3.2 Intellij

IntelliJ IDEA è un ambiente di sviluppo integrato (IDE) per il linguaggio di programmazione Java. IntelliJ permette di lavorare con numerose tecnologie e framework, tra i quali Spring.

Figura 2 - Logo di Intellij

3.3 Spring Boot

Spring è un framework open source per lo sviluppo di applicazioni su piattaforma Java con struttura modulare.

Spring Boot nasce con l’obiettivo di rendere più semplice e veloce lo sviluppo di applicazioni Spring.

Spring Boot permette di creare facilmente applicazioni stand-alone all’interno delle quali è possibile inserire le librerie di Spring e di terze parti, permettendo agli sviluppatori di concentrarsi meno sulla configurazione dell’ambiente.

Di seguito alcune delle principali feature di Spring Boot:

- Permette di creare applicativi Spring stand-alone;

- Incorpora direttamente i server Tomcat, Jetty o Undertow;

- Fornisce dipendenze base iniziali per semplificare la configurazione della build;

(19)

8

- Configura automaticamente le librerie Spring e di terze parti, quando possibile;

- Fornisce delle funzionalità come metriche e controlli di integrità;

- Creazione automatica dei file di configurazione di Maven.

Figura 3 - Logo di Spring

3.4 Hibernate

Hibernate è un ecosistema di librerie utilizzato in ambiente Java per rendere persistenti i dati tra l’ambiente Java ed il database di riferimento. Hibernate implementa le specifiche JPA (Java persistence API) per la persistenza dei dati.

La principale libreria cui di solito si fa riferimento con il termine Hibernate è Hibernate ORM: un framework open source di Object Relational Mapping che permette di operare sui database relazionali.

Figura 4 - Logo di Hibernate

3.5 Maven

Maven è uno strumento per la costruzione e la gestione di qualsiasi progetto basato su Java. L'obiettivo primario di Maven è permettere allo sviluppatore di comprendere e gestire efficacemente lo stato completo di un progetto di sviluppo software.

Per raggiungere questo obiettivo ci sono diverse aree che Maven indirizza:

- Fornisce un sistema uniforme di compilazione;

- Facilita il processo di compilazione, gestendo le dipendenze fra diverse librerie;

- Fornisce informazioni qualitative sul progetto;

- Fornisce linee guida per le migliori pratiche di sviluppo;

- Permette la migrazione trasparente verso nuove funzionalità.

Attraverso una elenco di comandi standard per eseguire le più tipiche fasi del ciclo di vita, Maven permette di gestire facilmente le varie fasi di creazione e sviluppo del software.

(20)

9

Figura 5 - Logo di Maven

3.6 MySQL

MySQL è un sistema gratuito open source per la gestione dei database relazionali (RDBMS). MySQL utilizza SQL (Structured Query Language) ed è compatibile con tutti i principali sistemi operativi.

MySQL è il database relazionale open source più popolare ed utilizzato al mondo. È ampiamente utilizzato per applicazioni web e soluzioni embedded.

Figura 6 - Logo di mySQL

3.7 GitLab

GitLab è una piattaforma web per la gestione di repository GIT, permette di salvare il proprio codice all’interno di repository pubblici o privati.

È principalmente utilizzato per la gestione del versionamento dei codici sorgente e come strumento di supporto per il branching, consente la collaborazione di più utenti sullo stesso repository con meccanismi di gestione dei conflitti.

Per il controllo e la gestione del flusso di lavoro e del branching è stato utilizzato il modello GitFlow.

Figura 7 - Logo di GitLab

(21)

10

3.8 Junit

Junit è un framework opensource usato per scrivere ed eseguire unit testing per Java.

Permette di rendere il codice più robusto e stabile mediante l’uso di test ripetuti, velocizzando anche le attività di debugging.

Junit fornisce asserzioni per testare i risultati attesi, annotazioni per identificare i metodi di prova e test runner grafici e testuali. Offre una suite per organizzare ed eseguire facilmente i test, e permette la condivisione dei risultati.

3.9 SonarLint

SonarLint è un’estensione gratuita ed open source IDE per il controllo del codice che supporta un elevato numero di linguaggi di programmazione e di Static Code

Analysis rules. Permette di effettuare test per identificare bug o vulnerabilità all’interno del codice e fornisce un supporto per la qualità e la sicurezza del codice.

Figura 8 - Logo di SonarLint

3.10 SonarQube

SonarQube è uno strumento che consente di produrre codice più pulito e sicuro:

effettua l’analisi del codice avendo come obiettivo la Code Quality e la Code Security, l’ispezione del codice avviene seguendo delle regole automatizzate già esistenti o customizzabili dall’utente.

SonarQube permette di avere un quadro completo sullo stato del codice a seguito dell’ispezione, fornendo allo sviluppatore un feedback comprensivo di bug, vulnerabilità, code smell, coverage, duplicazioni.

Per fornire una migliore affidabilità del codice, si concentra su cinque ambiti:

- Quality Gate: è un insieme di condizioni booleane basate su una collezione di misure, lo scopo è aiutare l’utente a capire nell’immediato se il progetto è pronto per la produzione mediante opportuni controlli;

- Rilevazione dei bug: i problemi rilevati da SonarQube sono generalmente originati da codice errato o che non produce il comportamento previsto;

(22)

11

- Code Smell: SonarQube è in grado di individuare i code smell all’interno del codice, cioè quelle porzioni di codice che sono in grado di effettuare quanto richiesto ma sono difficili da mantenere in quanto risultano di bassa qualità e potrebbero portare bug all’interno del codice, sono esempi di smell code:

codice duplicato, codice non coperto da test, codice troppo complesso;

- Vulnerabilità legate alla sicurezza: SonarQube aiuta a migliorare la sicurezza del codice scritto individuandone le vulnerabilità;

- Esplorazione di tutti i percorsi di esecuzione: lo strumento esplora tutti i possibili percorsi di esecuzione riuscendo ad individuare quanti più bug possibili.

Figura 9 - Logo di SonarQube

3.11 Swagger

Swagger è un Interface Description Language: è una libreria open source che

permette di documentare gli endpoint delle API RESTful, supporta i formati JSON e YAML.

Lo Swagger Editor è un editor accessibile tramite web che permette di descrivere le API REST ed avere un anteprima della documentazione prodotta, con eventuale segnalazione di errori.

Figura 10 - Logo di Swagger

3.12 Postman

Postman è un’applicazione del browser Google Chrome che consente di costruire, testare e documentare API più velocemente e sempificare gli step relativi al lifecycle di una API.

Fornisce una serie di tools per il design, il testing, la documentazione ed il mocking delle API; un repository per memorizzare, iterare e lavorare con più API da un’unica piattaforma; un workspace per organizzare le API e condividerle tra i membri dei team.

(23)

12

Utilizzando Postman è possibile effettuare delle chiamate API verso un server locale o verso un server online, impostando tutti i dati della chiamata: headers, body.

Alcune delle caratteristiche di Postman sono:

- Conserva la cronologia della chiamate effettuate;

- Consente di creare velocemente le API;

- Permette la personalizzazione mediante gli script;

- Robustezza dei framework dei test;

- Permett di creare delle collezioni di API.

Figura 11 - Logo di Postman

3.13 Artifactory

Artifactory è un prodotto di Jfrog per la gestione di repository binari; in generale il termine artefatto viene utilizzato come sinonimo del repository binario.

Artifactory è un repository di artifactory universale che permette di gestire diversi tipi di pacchetto nel ciclo di vita dello sviluppo di un applicazione, quindi permette di facilitare l’implementazione di un applicativo velocizzando il ciclo di rilascio del software.

JFrog Xray lavora con JFrog Artifactory per scansionare i pacchetti per le vulnerabilità nella sicurezza in ogni fase della pipeline DevOps.

Alcuni degli obiettivi di Artifactory sono i seguenti:

- Elevata scalabilità;

- Aumentare la produttività dello sviluppatore ed evitare il vendor-lock-in;

- Automazione: rilascio veloce ed automatizzato mediante pipeline;

- DevOps Management: permette la gestione, configurazione e monitoring dei servizi DevOps.

È disponibile in due versioni: Cloud Service, in cui Jfrog si occupa di gestire, mantenere e scalare l’infrastruttura del repository e fornisce backup del server automatici, e Self-Hosted, in cui l’installazione, la gestione ed il mantenimento del repository sull’hardware personale o sul cloud sono lasciati all’utente.

(24)

13

Figura 12 - Logo di JFrog Artifactory

3.14 Jenkins

Tool per Continuous Build e Continuous Integration utilizzato per il rilascio in ambiente di sviluppo: si occupa di svolgere e monitorare dei task ripetitivi, come ad esempio processi di building dei progetti software, permettendo allo sviluppatore di concentrarsi sullo sviluppo del progetto.

L’insieme dei task eseguiti compone un job. Un job viene eseguito a seguito di una commit sui branch relativi ai vari ambienti di sviluppo per effettuare degli ulteriori controlli sul codice prima che venga rilasciato, è possibile definire pipeline di task diverse per ogni ambiente.

L’amministrazione di Jenkins ed il suo monitoraggio avviene attraverso una interfaccia web.

Ricercando il progetto d’interesse si ha già un primo feedback sullo stato tramite il colore dell’icona a sinistra (grigio non buildato, rosso build fallita, blu build riuscita).

Oltre questo primo input è possibile verificare nel dettaglio alcune anomalie tramite l’output di console (diponibile nel menu che viene visualizzato dopo aver selezionato il progetto). Questo output mostra un elenco dettagliato delle operazioni eseguite oltre che la segnalazione di eventuali errori riscontrati. L’analisi dei log permette, nella maggior parte dei casi, di determinare se il problema di build è legato alla struttura del job o a problemi relativi alla struttura del progetto facilitando cosi la risoluzione. In caso di build positiva dal job stesso saranno raggiungibili i link per Artifactory e per Sonarqube.

Figura 13 - Logo di Jenkins

(25)

14

3.15 Redmine

Redmine è un software gratuito e open source per la pianificazione dei progetti ed il tracciamento di segnalazioni tramite interfaccia web. È un tool di activity

management che permette di tracciare e coordinare le varie attività che devono essere svolte su un progetto, favorendone sia la pianificazione, che lo sviluppo, che il rilascio nei vari ambienti.

Figura 14 - Logo di Redmine

(26)

15

Capitolo 4

Architetture applicative

L’architettura di un applicativo ne definisce la struttura incidendo sulle scelte di implementazione e sviluppo. Questo questo capitolo è dedicato all’analisi delle principali tipologie di architetture applicative: monolitica, SOA ed a microservizi.

4.1 Architettura monolitica

L’architettura monolitica, come si può evincere dal nome, è composta da un unico blocco, tutti i componenti dell’applicativo sono implementati in un singolo

programma che risiede su una singola piattaforma, essi sono interconnessi ed

interdipendenti tra loro. Generalmente un’applicazione monolitica è molto semplice e consiste di tre elementi principali: un client, un server ed un database.

(27)

16

Figura 15 - Architettura Monolitica

Questa tipologia architetturale è adatta al lavoro in team di dimensioni ridotte e limitate in cui è semplice organizzare il lavoro limitando i conflitti dovuti alla parallelizzazione dei task; per questo motivo è un buon punto di partenza per lo sviluppo di applicativi per piccole aziende, ad esempio sono un’ottima scelta per le startup che necessitano di un prodotto che sia pronto in tempi brevi.

I vantaggi di un applicativo monolitico sono i seguenti:

- Sviluppo e deploy semplice: un unico deploy per rilasciare ed aggiornare le funzionalità dell’applicazione;

- La gestione di audit, logging, rate limiting e aspetti simili sono limitate ad un singolo server, quindi più semplici;

- Se costruita correttamente può garantire prestazioni migliori rispetto ai microservizi, ciò in quanto questi ultimi necessitano di effettuare varie chiamate ad API per costruire una singola pagina o erogare un servizio e ciò rallenta le performance, mentre nelle applicazioni monolitiche la

comunicazione tra i componenti è veloce essendo il codice su un unica piattaforma.

(28)

17 Analizziamo di seguito gli svantaggi:

- Nel tempo, il codice può incrementare e diventare complesso da gestire su un unico blocco: generalmente le applicazioni crescono con il tempo per

implementare nuove funzionalità e venire incontro alla crescita dell’azienda, di conseguenza un’architettura monolitica può diventare inefficiente in termini di gestione della complessità del codice, che diviene difficile da capire e

modificare, specie se nuovi developers si aggiungono al progetto, e la qualità del codice diminuisce;

- Adottare nuove tecnologie è difficile: l’aggiunta di nuove tecnologie può richiedere una riscrittura totale del codice per adattarlo ad esse e ciò richiede costi e tempi aggiuntivi;

- Ogni modifica o aggiornamento richiede un redeploy di tutto l’applicativo, motivo per cui si tende ad accumulare modifiche prima di rilasciare una nuova versione e ciò diminuisce la dinamicità con cui evolve l’applicativo.

4.2 Service-Oriented Architecture (SOA)

Le architetture orientate ai servizi sono utilizzate nel caso di applicativi che è possibile scomporre in più blocchi, ognuno implementante una funzionalità dell’applicativo, che lavorano in maniera combinata al fine di erogare i servizi richiesti.

Le architetture orientate ai servizi nascono come risposta alla necessità di organizzare i sistemi complessi in software adatti ad ambienti distribuiti ed eterogenei, ambienti che internet ha reso sempre più frequenti, per ovviare alle problematiche riscontrate nello sviluppo di sistemi basati su architetture monolitiche.

Le architetture SOA sono un modello architetturale utile per l’implementazione di sistemi distribuiti, cioè sistemi composti da più servizi indipendenti, definiti loosely coupled; ogni servizio implementa una funzionalità dell’applicativo. Al fine di

integrare e far comunicare i servizi sono stati implementati dei protocolli e delle interfaccie standard; inoltre, risulta fondamentale l’utilizzo di service bus per la gestione delle comunicazioni. Un service bus è un sistema per interconnettere i componenti distribuiti e le piattaforme su cui sono installati permettendone

l’interoperabilità; esso permette la connessione tra i componenti, il trasferimento di dati e messaggi, servizi di routing, sicurezza, gestione sei servizi e dei log.

Nell’articolo [4] di System Innovation Accademy vengono proposti due esempi utili al fine di comprendere cos’è un sistema orientato ai servizi e come viene stutturato:

- Nel primo esempio viene proposta una situazione interna al mondo IT, nello specifico si idealizza lo sviluppo di un’applicazione web che permetta il pagamento dei ticket di un parcheggio: questa applicazione richiederà varie

(29)

18

funzionalità e, quindi, componenti, ad esempio per il login, per il pagamento, per l’autenticazione, per la localizzazione, etc. Utilizzando un’architettura SOA è possibile implementare ogni singolo componente da zero oppure utilizzare i servizi offerti da altri enti, come ad esempio Google’s map per la localizzazione, Paypal per il pagamento, Facebook per il login. La seconda opzione permette di ridurre notevolmente i tempi di sviluppo assemblando ed integrando

componenti già implementati da terzi e rispecchiando la finalità per cui nascono i sistemi orientati ai servizi.

- Nel secondo esempio, invece, viene paragonato un sistema con architettura SOA ad un qualsiasi servizio reale, ad esempio un bar: ogni servizio offerto all’interno del bar è indipendente dagli altri ed è associato ad un componente (l’impiegato che prepara il caffé o il cibo, la sedia che permette ai clienti di sedersi, i comfort dell’ambiente, etc.). Il focus in questo esempio si sposta sulla figura dell’architetto della struttura di un sistema orientato ai servizi, il quale, alla pari del proprietario del bar, non è interessato a come ogni singolo task viene svolto, né lo implementa esso stesso, bensì è interessato a come questi servizi cooperino e vengano integrati al fine di fornire le funzionalità richieste e sulla base di alcuni requisiti. Questo esempio ci permette di comprendere un’altra caratteristica delle architetture SOA: ciò che è all’interno di ogni componente non è competenza del designer.

I servizi sono indipendenti, possono, infatti, essere implementati da aziende o team distinti e supportare linguaggi e tecnologie diversi, purché siano noti i protocolli e le interfaccie per la comunicazione e l’interoperabilità degli stessi.

Questa tipologia di architettura è adatta ad aziende con sistemi abbastanza complessi, come nel caso di banche o sistemi difficili da suddividere in microservizi. In generale, sono più sicure rispetto alle strutture monolitiche in cui ho un singolo point-of- failure.

È utilizzabile per sistemi complessi che vengono, quindi, organizzati su più software che lavorano in modo indipendente ma collaborano per implementare le funzionalità richieste.

Anche in questo caso, può essere utile fare una lista di quali sono gli aspetti positivi e negativi di questo modello architetturale.

Di seguito l’elenco dei vantaggi:

- I servizi sono riutilizzabili: ogni servizio viene organizzato in modo quasi indipendente dagli altri ed implementa una funzionalità a sé, motivo per cui i componenti di un’applicazione SOA possono essere riutilizzati per altri applicativi che richiedono la stessa funzionalità senza compromettere gli altri servizi;

(30)

19

- Alta affidabilità e gestione più semplice: sono più semplici da debuggare rispetto alle architetture monolitiche, in quanto il codice è organizzato in maniera più efficiente relativamente alle funzionalità erogate;

- Scalabilità: è più facile ampliare l’applicativo partendo dall’architettura

esistente, non essendo necessario un deploy di tutta l’applicazione ma solo un lavoro di integrazione;

- Disponibilità: questa caratteristica è riconducibile alla possibilità di distribuire su più server il codice e, di conseguenza, non avere un singolo point of failure, come nel caso delle architetture monolitiche;

- Sviluppo in parallelo: questo tipo di architettura permette di parallelizzare lo sviluppo dell’applicativo a livello di codice, in quanto strutturato su più livelli, quindi va bene per team con un elevato numero di sviluppatori.

Gli svantaggi:

- Gestione complessa: suddividere l’applicativo in microservizi è un procedimento complesso, inoltre questi vanno gestiti ed interconnessi;

- Costi ed investimenti elevati in termini di risorse umane, tecnologie e sviluppo;

- Può risultare meno efficiente in termini di tempo ed avere maggiore latenza nella risposta dei servizi, questo in quanto il codice è distribuito su più server che devono comunicare tra loro e ciò può richiedere tempo aggiuntivo di risposta e quindi ridurre le performance, non va bene per sistemi real time;

- Si è vincolati a chi vende il servizio se questo viene acquistato da terzi.

4.3 Architetture a Microservizi

Un’applicazione orientata ai microservizi consiste di un insieme di componenti indipendenti che interagiscono mediante una serie di API. Questa tipologia di architetture è molto simile alle architetture SOA precedentemente descritte,

potremmo definirle una tipologia di architetture SOA con un particolare focus verso l’autonomia dei componenti.

L’architettura a microservizi è diventata sempre più utilizzata negli ultimi anni in cui le aziende hanno iniziato ad utilizzare un approccio agile e ad adottare la metodologia DevOps. Il modello DevOps mira a rimuovere le barriere tra il team di sviluppo ed il team operativo e si integra perfettamente con questa tipologia di architettura in quanto prevede l'integrazione continua e la distribuzione continua del software, l’utilizzo dei microservizi rende i rilasci più flessibili e veloci rendendo lo sviluppo più agile.

Citando Luca Kraus: i microservizi sono importanti perché aggiungono un valore semplificando la complessità del sistema, rompendo il sistema o un applicazione in

(31)

20

piccole parti, riducendo le duplicazioni, aumentando la coesione e l’indipendenza tra i componenti, rendendo il sistema più semplice da capire, più scalabile e più facile da modificare.

La granularità dei componenti è una scelta molto rilevante nell’ambito delle

architetture orientate ai microservizi: ogni unità deve implementare una funzionalità ed essere quanto più indipendente dalle altre per permettere di sviluppare e rilasciare in modo parallelo i microservizi, mirando inoltre a ridurre la duplicazione

nell’implementazione delle funzionalità.

I servizi possono essere distribuiti su internet, su uno o più server. I servizi possono condividere il database o utilizzarne uno per ogni unità.

Di seguito un’analisi dei vantaggi dell’architettura a microservizi:

- Facile da sviluppare, testare e deployare: i servizi possono essere sviluppati e rilasciati in modo indipendente dagli altri, inoltre, essendo i componenti di dimensioni limitate, le unità da deployare sono piccole e ciò facilita e velocizza il rilascio di nuove funzionalità o la modifica di quelle già esistenti,

permettendo uno sviluppo ed un deploy più dinamico. Le unità sono quasi del tutto indipendenti da rilasciare. Questo riduce i rischi di ogni singolo rilascio rispetto al funzionamento dell’applicativo, in quanto incide solo su un

componente di dimensioni limitate.

- Agilità: il lavoro di sviluppo e rilascio all’interno del team può essere

parallelizzato in quanto i microservizi sono indipendenti, ciò riduce i conflitti e permette di lavorare in maniera più autonoma;

- Disponibilità: tollera bene gli errori, non ha un single point of failure;

- Scalabilità orizzontale: si intende la scalabilità in termini di microservizi creati all’interno dello stesso contenitore, questa non è limitata e i servizi possono essere eseguiti dinamicamente, inoltre se un servizio risulta “pesante” in quanto ha un numero elevato di richieste può essere scalato/suddiviso ulteriormente in microservizi;

- È possibile utilizzare tecnologie differenti su microservizi distinti.

Tra gli svantaggi possiamo evidenziare i seguenti:

- Complessità: strutturare l’architettura e dividere l’applicativo in microservizi è un lavoro complesso, richiede la gestione di più artifacts e l’elaborazione di una pianificazione del lavoro;

- La consistenza dei dati deve essere gestita;

- Sicurezza: ogni microservizio comunica con gli altri mediante API, ciò aumenta le probabilità di un attacco, perciò bisogna implementare delle misure di

sicurezza appropriate al fine di limitare la possibilità che si verifichi;

- Gestione di mantenimento, monitoraggio, log e failures complessa;

- Può avere un’alta latenza.

(32)

21

I microservizi sono adatti soprattutto nel caso di applicazioni che evolvono e sistemi complessi. Bisogna prenderli in considerazione quando si ha un team ampio e con diverse esperienze in campo tecnologico e per l’implementazione di applicazioni complesse e divisibili in servizi.

Quando un’applicazione è di dimensioni elevate utilizzare i microservizi può apportare un beneficio in termini di scalabilità e flessibilità.

Approfondiremo questo argomento nel capitolo successivo.

4.4 Confronto

L’obiettivo di questo paragrafo è porre a confronto i modelli architetturali precedentemente analizzati, cercando di evidenziarne le differenze al fine di

comprendere in quali occasioni utilizzarli e come scegliere l’architettura più adatta al nostro progetto.

Figura 16 - Confronto Architetture

Le applicazioni con architettura monolitica si differenziano dalle altre in quanto possono essere considerate come un singolo sistema privo di dipendenze esterne ed in grado di offrire i suoi servizi autonomamente, al contrario delle architetture

distribuite in cui generalmente il sistema è eseguito su più server in parallelo che collaborano al fine di erogare i servizi e le funzionalità dell’applicativo.

L’architettura monolitica è la più semplice da implementare, ma è poco scalabile, in quanto non risulta efficiente in termini di performance per applicazioni con un

elevato numero di servizi e di utenti. Questa tipologia di struttura, inoltre, aumenta la

(33)

22

possibilità che si verifichino dei conflitti nella fase di sviluppo se si lavora in parallelo.

Di base, sarebbe meglio optare per un’architettura monolitica nel caso di applicativi semplici che non richiedano un’elevata scalabilità, generalmente è la tipologia scelta dalle aziende nella fase iniziale.

L’architettura SOA consiste di più endpoint su cui sono suddivisi i servizi e le funzionalità offerti dall’applicativo. Quindi l’applicativo è distribuito su più server che interagiscono tra loro per implementare le varie funzionalità ed erogare i servizi richiesti.

Le architetture a microservizi, analogamente alle architetture SOA,

distribuiscono i servizi dell’applicativo su più server, la differenza è da ricercare nel livello di granularità e modularità dei servizi in cui è suddiviso l’applicativo, in quanto, seppur non ci sia una regola fissa, in questa tipologia di architettura la dimensione dei microservizi è rilevante ed è scelta in maniera attenta. Un microservizio è un unità indipendente che può racchiudere un dominio o implementare una singola funzionalità.

Generalmente le dimensioni di un microservizio sono più piccole rispetto ai servizi su cui sono distribuite le architetture SOA, vi è una maggiore granularità e ciò può apportare benefici su larga scala.

La differenza sostanziale tra le architetture SOA e quelle a microservizi è che nel secondo caso c’è uno studio più attento relativamente alle dimensioni di un

microservizio, mentre nel primo caso questa analisi viene meno, si ha solo l’esigenza di suddividere l’applicativo su più server per una maggiore efficienza e

parallelizzazione. Nelle architetture SOA lo scopo è quello di avere una struttura organizzata in moduli autonomi, facili da mantenere e aggiornare, e che siano riutilizzabili; il focus non è sui benefici relativi alle dimensioni limitate dei singoli blocchi.

La differenza principale tra le architetture monolitiche e distribuite è la distribuzione del codice su uno o più repository, dipende dalle dimensioni dell’applicativo: in generale, è meglio optare per un’architettura monolitica per applicazioni semplici;

solitamente le aziende quando nascono partono da un’applicazione monolitica per poi, se necessario, spostarsi su architetture SOA o a microservizi suddividendo

l’applicativo in più servizi. Le architetture SOA e a microservizi sono usate per sistemi distribuiti su larga scala.

Nel caso in cui abbiamo un sistema di dimensioni elevate e la necessità di parallelizzare il lavoro tra vari team, è meglio optare per un’architettura a microservizi piuttosto che SOA, in quanto permette di parallelizzare il lavoro in maniera più efficacie limitando i conflitti.

(34)

23

Capitolo 5

Microservizi

L’obiettivo di questo capitolo è approfondire l’analisi delle architetture a microservizi, essendo state oggetto del progetto di tirocinio, concentrandoci sull’organizzazzione, lo sviluppo ed il rilascio dei servizi.

5.1 Cosa differenzia i microservizi dalle altre architetture?

Analizziamo innanzitutto quali sono le differenze principali che ci permettono di distinguere il modello di architettura a microservizi dagli altri.

Un microservizio è considerato come un’unità di codice autonoma di dimensioni limitate, questo è generalmente focalizzato sull’esecuzione di un singolo compito o di una singola funzionalità. La dimensione limitata dei microservizi è una delle differenze principali che li distingue dalle altre architetture monolitiche o distribuite:

nel primo caso abbiamo, infatti, un unico blocco in cui è contenuto tutto il codice, quindi tutte le funzionalità dell’applicativo, questo comporta che con la crescita delle dimensioni del software il codice diventi complesso da comprendere e modificare, è bene fissare un limite alla dimensione di ogni servizio per migliorare la gestione del codice.

Robert C. Martin definisce il Principio delle Singole Responsabilità (Single

Responsability Principle), in cui definisce un microservizio come un’unità di codice coeso che tiene insieme le cose che cambiano per lo stesso motivo e separa ciò che

(35)

24

cambia per motivi diversi. In sostanza, il codice di un microservizio deve essere finalizzato ad una stessa funzionalità ed essere internamente correlato. Le funzionalità le cui modifiche sono correlate andrebberò aggregate in un unico microservizio.

In “Building Microservices, designing fine-grained systems” di Sam Newman, viene proposto l’interrogativo più ricorrente nell’ambito dei microservizi: “Quanto deve essere piccolo un microservizio?”, non c’è una risposta definitiva a questo quesito, bensì risulta essere ancora un problema aperto, in quanto non è facile definire un riferimento sulla base del quale dimensionare il microservizio.

Vediamo di seguito alcune proposte e le motivazioni per cui queste non sono state identificate come soluzioni definitive al problema:

- Linee di codice: misurare il microservizio sulla base delle linee di codice non darebbe un’idea oggettiva della grandezza del codice, in quanto questa la quantità misurata sarebbe strettamente legata al linguaggio utilizzato ed alla sua sinteticità;

- Tempo: è stata proposta da Jon Eaves una misura in termini di tempo, descrivendo il microservizio come un’unità di codice per la cui

implementazione occorrono non più di due settimane, ma anche in questo caso ci sarebbero altri fattori da considerare;

- Team: un’altra opzione proposta per dimensionare i microservizi è relativa ad un fattore organizzativo, cioè alla dimensione del team che si occupa dello sviluppo del servizio, definendo il microservizio come un blocco di codice implementabile da un team di dimensioni ridotte, questo perché uno dei principali benefici che i microservizi dovrebbero apportare è la possibilità di parallelizzare il lavoro riducendo i conflitti, ma anche in questo caso la dimensione del team non è ben definibile.

In generale, più è piccolo un microservizio, più si beneficia delle caratteristiche che contraddistinguono questa architettura, ma allo stesso tempo aumentando la

granularità aumenta il numero di microservizi in cui è suddiviso il software e questo comporta una maggiore complessità nella gestione delle varie unità ed una maggiore quantità di API a cui fare riferimento, cosa che potrebbe aumentare la latenza di risposta. Bisogna trovare una dimensione tale da poter gestire questa complessità.

Di seguito, un elenco di alcuni fattori che potrebbero influenzare la suddivisione del nostro sistema in microservizi determinandone le dimensioni:

- Isolare i blocchi di codice per cui sono previste modifiche correlate o che lavorano sullo stesso database, in modo da isolare gli aggiornamenti alla singola unità e permettere un rilascio più veloce;

- La struttura del team, anche considerando la possibilità di avere un team distribuito geograficamente;

(36)

25

- Il livello di sicurezza necessario al singolo microservizio, si può incrementare la sicurezza, se necessario, per la singola unità;

- Le tecnologie utilizzate;

- Dipendenze tra i blocchi di codice: bisogna separare i servizi non correlati, ma limitare l’interdipendenza tra i servizi sia per una questione di sviluppo

autonomo, sia per evitare latenze relative ai tempi di risposta dei servizi chiamati;

- Il database: bisogna considerare la struttura dei dati, avere un database dedicato al microservizio può facilitarne la gestione, purché il sistema resti consistente. È possibile ricorrere all’utilizzo di livelli intermedi utilizzando dei framework come Hibernate per legare il codice al database, facilitando il

mapping degli oggetti, questo può anche essere utile per capire quali dati siano effettivamente utili al microservizio.

Altri aspetti rilevanti da gestire in un’architettura a microservizi sono

l’indipendenza e l’autonomia del singolo servizio in relazione agli altri, sia a livello di sviluppo che di deploy. Questo implica che ogni servizio deve poter essere modificato senza comportare altre modifiche sugli altri, i servizi non devono essere correlati; ciò che viene condiviso sono le API associate ad ogni servizio. Le API costituiscono l’interfaccia necessaria alla comunicazione tra i microservizi, ma non incidono in alcun modo sulle scelte relative allo sviluppo del servizio, sono, infatti, indipendenti dai metodi di sviluppo e dalle tecnologie utilizzate all’interno del microservizio, si limitano a definirne input ed output.

5.2 Benefici delle architetture a microservizi

Essendo questa tipologia di architettura un’evoluzione delle architetture dei sistemi distribuiti, tra i benefici chiave dei microservizi ve ne sono alcuni comuni, legati alla modularità dei servizi, che vengono portati ad un livello superiore nel caso dei microservizi.

I microservizi permettono l’utilizzo di tecnologie diversificate per ogni unità, il che permette di decidere quale tecnologia utilizzare a seconda delle necessità e quindi scegliere il tool adatto ad ogni microservizio in modo specifico, senza dover limitare la scelta a quelli che sono adatti a tutti i microservizi in modo più generico. Quindi è possibile scegliere la tecnologia che ottimizza le performance di ogni singolo servizio.

Lo stesso principio è applicabile alle scelte relative alla conservazione dei dati e alla struttura dei database, ogni microservizio può, infatti, avere un proprio database o condividerlo, ciò permette di strutturare ed organizzare i dati in modo funzionale all’esigenza del singolo servizio.

(37)

26

Nel caso di architetture monolitiche abbiamo un singolo point-of-failure, ciò comporta che se il servizio si rompe tutto l’applicativo smette di funzionare,

distribuendo, invece, il sistema su più macchine si risolve questa problematica; nei microservizi è possibile gestire i casi in cui un servizio smette di funzionare in

maniera isolata con un sistema di gestione delle failure. È importante capire come il fallimento di un microservizio impatti sugli altri al fine di limitare la propagazione della failure.

Relativamente al rilascio di blocchi di codice: nelle architetture monolitiche era necessario rilasciare tutto l’applicativo ad ogni modifica o aggiunta sul codice, il che richiede un costo in termini di tempo e porta ad accumulare malfunzionamenti nel lungo termine, in quanto c’è la tendenza a raggruppare più aggiornamenti in un unico rilascio ritardando, quindi, le correzioni e l’aggiunta di nuove funzionalità; nelle architetture a microservizi, invece, ogni microservizio viene rilasciato

autonomamente rendendo rilasci e rollback più veloci e semplici, questo comporta una maggiore dinamicità e flessibilità.

I sistemi distribuiti hanno tra gli intenti quello di poter riutilizzare i servizi, questo è possibile anche nell’ambito dei microervizi, in più modi e per diversi scopi. Ciò è molto rilevante se pensiamo alla varietà di dispositivi su cui potrebbe essere utilizzato il nostro applicativo (web, native application, mobile web, tablet, etc.). I microservizi possono essere ricomposti in modo tale da soddisfare la funzionalità richiesta.

Due caratteristiche importanti dei sistemi distribuiti sono la capacità di disaccoppiare i servizi, rendendoli quanto più possibile indipendenti ed autonomi rispetto agli altri, e l’alta coesione del codice e delle funzionalità all’interno del singolo microservizio: ho un buon livello di disaccoppiamento quando è possibile apportare una modifica ad un servizio e deployarlo senza la necssità di dover cambiare altre parti del sistema;

mentre, quando parliamo di high cohesion ci riferiamo al fatto che vogliamo che tutte le funzioni interne ad un microservizio cooperino per erogare una stessa funzionalità, quindi all’interno del singolo servizio non dobbiamo avere funzioni slegate.

Dunque, da un lato bisogna definire e strutturare ogni microservizio in modo tale che sia quanto più autonomo possibile, cioè creare servizi autosufficienti con un numero limitato di chiamate verso altri servizi, così da aumentare il livello di

disaccoppiamento e rendere sviluppo e deploy indipendente; dall’altro all’interno di un microservizio bisogna racchiudere solo funzioni correlate e strettamente legate tra loro. L’obiettivo è avere microservizi autosufficienti e, quindi, capire quali sono i confini che racchiudono una funzionalità all’interno dello stesso servizio, cioè fin dove delimitare parte del codice in un unico microservizio per essere certi che tutti i

comportamenti e le funzioni correlate siano in un unico blocco e che si limiti il numero di chiamate ad API.

La modularità dei microservizi permette di acquistare ed integrare software da terzi nel caso di tool standard, COSS (commercial-off-shelf software), SaaS (Software as a

(38)

27

Service), o altre tipologie. L’acquisto del software riduce la customizzazione del servizio ed il controllo sullo stesso, è una buona opzione quando l’implementazione autonoma non aggiungerebbe nulla al servizio rispetto al modello standard;

acquistare da terzi riduce i tempi di sviluppo.

5.3 Integrazione

Un’argomento chiave quando parliamo di microservizi è l’integrazione. Il concetto alla base dei microservizi è quello di cotruire un sistema composto da moduli autonomi al fine di avere un sistema flessibile e aperto ai cambiamenti, deve essere possibile introdurre modifiche o l’uso nuove tecnologie in un microservizio senza che questo impatti gli altri, ciò altrimenti causerebbe un costo aggiuntivo che limiterebbe la flessibilità del nostro sistema e ci porterebbe a ridurre le modifiche attuate per paura di introdurre un costo e accumulare i technical debts sulla nostra applicazione.

L’integrazione deve essere fatta in modo tale da mantenere la struttura del nostro applicativo flessibile ed i microservizi autonomi, ma soprattutto una modifica su un microservizio non deve impattare gli altri o portare regressioni sul sistema.

5.4 Database

È possibile effettuare integrazioni anche relativamente al database, tuttavia richieste di dati o di scritture verso un servizio che comunica con un database inevitabilmente vengono strutturate in base ai dati in esso contenuti, una modifica al database può quindi richiedere delle modifiche al codice, sia sul server che lato client, quindi va eseguita in modo consapevole tale da non impattare negativamente sul sistema.

Se i servizi condividono il database su cui operano, viene aggiunto un fattore di

correlazione tra essi, ciò riduce l’autonomia di ogni unità facendo perdere le proprietà di coesione e disaccoppiamento dei microservizi. L’utilizzo di un database integrato è sconsigliata nelle architetture a miroservizi.

5.5 Comunicazione tra microservizi

I servizi possono comunicare in modo sincrono o asincrono: in una comunicazione asincrona le chiamate vengono eseguite verso un server remoto, il client resta in

(39)

28

attesa della risposta dal server finché non termina; in una comunicazione asincrona il chiamante non deve attendere il termine dell’operazione per ritornare.

Le comunicazioni sincrone sono più semplici da gestire e da implementare, in quanto è noto quando il risultato è pronto per essere utilizzato, ma sono bloccanti; al

contrario, le comunicazioni asincrone sono più complesse da gestire ma non sono bloccanti, quindi la UI rimane arriva e risponde ai comandi.

Nel caso sincrono, le comunicazioni sono di tipo request-response, nel caso asincrono sono event-based. Nel primo caso chi invia una richiesta deve attendere la risposta del server; il secondo caso permette sia di implementare un’attesa della response, sia di implementare una richiesta al server non bloccante, in questo caso il server esegue un’operazione quando si verifica un evento, il client non deve iniziare una

comunicazione o eseguire una richiesta, questo tipo di comunicazione è definita event-based e garantisce un elevato disaccoppiamento.

All’interno del suo libro sui microservizi, Sam Newman paragona una comunicazione sincrona tra i servizi ad un’orchestra che va guidata dall’esterno, mentre una

comunicazione asincrona ad una coerografia in cui ogni componente ha un ruolo e conosce la sua parte, quindi sa come reagire a determinati eventi.

In generale, per aumentare l’indipendenza di un sistema a microservizi, una

collaborazione asincrona tra i componenti aumenta il disaccoppiamento dei servizi, è più flessibile, rende il sistema più scalabile ed, inoltre, le modifiche al servizio

impattano meno il chiamante, ma richiede un costo aggiuntivo per tenere traccia dei processi. La collaborazione sincrona ha costi di modifica più alti ed è meno flessibile, ma sicuramente più semplice da implementare.

È possibile avere un sistema che utilizzi entrambi i tipi di comunicazione, capendo quali tecnologie associare ad ogni approccio.

Tra le tecnologie più comuni nell’ambito delle comunicazioni sincrone (request- response) abbiamo RPC (Remote Procedure Calls) e REST (Representational State Transfer):

- RPC si comporta come segue: viene fatta una chiamata da un consumer in locale, questa viene indirizzata ed eseguita su un server remoto. Una delle tecnologie utilizzate per questo tipo di implementazione è SOAP. RPC permette di avere un’interfaccia intermedia per adattare le chiamate alla tecnologia con cui sono poi implementate sul server remoto. Ogni tecnologia ha un formato per definire i messaggi di request-response, che può essere, ad esempio, binario o XML, come nel caso di SOAP; alcune tecnologie poggiano su

protocolli network, nel caso di SOAP viene utilizzato HTTP. In generale queste tecnologie permettono di avere una comunicazione client-server attraverso la rete molto veloce.

(40)

29

La chiamata a remoto richiede un tempo di elaborazione più elevato a causa dell’attraversamento della rete per cui è necessario un processo di marshalling e unmarshalling, la gestione della sicurezza nelle comunicazioni attraverso la rete e le performance. Bisogna essere consapevoli delle debolezze di queste tecnologie ed utilizzarle con criterio.

- REST è uno stile architetturale di comunicazione ispirato al web. Il server crea diverse rappresentazioni delle richieste lato client, l’elaborazione della richiesta dal client segue diversi modelli a seconda della funzionalità richiesta (GET, POST, PUT, DELETE, etc.); l’utilizzo del protocollo REST facilita

l’elaborazione delle richieste sul client mediante l’utilizzo di metodi semplici che vengono poi elaborati sul server per soddisfare la richiesta.

È possibile utilizzare un sistema di caching in REST over HTTP per rendere più veloci le chiamate ai servizi. Esistono framework a supporto di questa tipologia di chiamate, come ad esempio SpringBoot.

A livello di performance REST over HTTP può risultare più compatto rispetto a SOAP, in quanto supporta più formati come JSON o formati binari, ma il protocollo HTTP può comportare delle latenze nella comunicazione per volumi di traffico elevati.

5.6 User Interface

5.6.1 Aggregazione dei componenti

La user interface di un applicativo è generalmente composta da più elementi o widget, nel caso dei microservizi richiede quindi molteplici chiamate verso gli endpoint dei componenti necessari per assemblare la pagina richiesta, è possibile adottare due diversi approcci: caricare ogni singolo componente mediante la relativa chiamata a servizio, oppure aggregare più chiamate in un API gateway che riporta a frontend più componenti per poi assemblarli al fine di creare la UI richiesta. Un’altra opzione può essere assemblare più widget in un componente più grande che faccia da livello intermedio.

In generale, è necessario implementare un livello intermedio per assemblare i componenti richiesti.

Il vantaggio di caricare più componenti aggregrati con una singola chiamata è anche collegabile ad un aspetto organizzativo, soprattutto per il lavoro in team, in quanto si associano i componenti del frontend ai relativi servizi del backend che andranno

(41)

30

assegnati ad un unico team di sviluppo in modo tale da facilitare la comunicazione interna e lo sviluppo.

Questo sistema ha anche degli svantaggi:

- La user experience potrebbe non essere ottimale, in quanto l’utente vedrebbe parti diverse dell’interfaccia lavorare in modo indipendente e distinto, talvolta anche con un design diverso. Tuttavia è possibile ovviare applicando delle tecniche.

- Non è semplice da gestire, soprattutto nel caso di applicazioni native in cui si richiede di avere il singolo componente UI, in tal caso è preferibile un

approccio ibrido.

5.6.2 Backends for Frontends

Una soluzione alla comunicazione tra frontend e backend applicabile sia nel caso precedentemente discusso di chiamate multiple verso più endpoint, sia nel caso in cui sia necessario adattare la risposta del backend al device e all’interfaccia, è avere un livello intermedio lato server che si occupi di aggregare i risultati di più API: quindi, si occupa di fare chiamate lato server verso uno o più servizi per aggregarne ed

organizzarne i contenuti, in modo tale da restituire al frontend un aggregato di più componenti. Questo livello può essere visto come un orchestratore dei livelli sottostanti.

Generalmente, nelle architetture ai microservi è preferibile avere BFF multipli, in quanto avere un livello unico che aggreghi tutti i livelli sottostanti eliminerebbe le proprietà di disaccoppiamento ed indipendenza dei microservizi precedentemente discusse; la soluzione più adatta a questa tipologia di sistemi è optare per un modello con BFF multipli, uno per ogni blocco o widget, quindi BFF dedicati.

5.7 Continuous Integration e Continuous Delivery

La Continuous Integration è un elemento da considerare quando parliamo di microservizi, è un concetto relativo alla parte di sviluppo e rilascio del software, quindi quando parliamo di build, versionamento, modelli per la gestione e l’allineamento dei microservizi.

L’obiettivo della CI è mantenere i microservizi allineati tra loro e funzionanti, qundi essere certi che il rilascio di nuovo codice si integri correttamente con il codice già

(42)

31

presente rilasciato in precedenza sul repository. Un server CI controlla il codice dopo il commit e verifica che compili e che i test passino.

Come parte del processo di Continuous Integration vengono creati gli artifacts, cioè dei pacchetti che serviranno per un ulteriore validazione del codice, come passaggio aggiuntivo per il deploy di un servizio, infatti, questo viene nuovamente testato prima di essere installato sulla piattaforma relativa. Viene creato un artifact per ogni

versione del microservizio, di modo da essere certi che la versione ed il codice

installati siano quelli testati in precedenza. Questi pacchetti vengono conservati in un repository.

Gli artifacts potrebbero necessitare di tool e piattaforme di supporto. Gli OS artifacts sono i sistemi più funzionali che permettono di nascondere la tecnologia utilizzata dal microservizio.

Utilizzare la CI comporta vari benefici:

- permette di avere un feedback immediato sulla qualità del codice, quindi poter allo stesso tempo individuare eventuali parti da correggere su porzioni limitate di codice, rendendo più semplice individuare l’errore;

- permette di automatizzare la creazione di artifact binari;

- il codice è versionato quindi è possibile ricreare l’artifact.

La chiave dell’applicazione dell’integrazione continua è capire come applicarla realmente e non limitarsi ad utilizzare i tool a disposizione. Possiamo seguire queste regole:

1. Bisogna controllare la mainline una volta al giorno: bisogna essere certi che il codice sia stato integrato altrimenti avremmo codice accumulato. Bisogna integrare costantemente il codice al branch principale;

2. I cambiamenti al codice vanno validati e testati, bisogna evitare di rompere il codice e il sistema: CI senza validazione non è CI;

3. Quando una build si rompe ed il pacchetto artifact non viene creato, va messa come priorità la correzione del codice: non bisogna accumulare build

“rotte”, bisogna correggere immediatamente l’errore. Se lasciamo più build da sistemare accumuliamo gli errori ed il tempo per correggere le build aumenta.

Capito cos’è la CI bisogna capire come questo viene integrato ed applicato ai

microservizi. Ribadendo che vogliamo mantenere l’autonomia di modifica del codice e deploy di ogni microservizio, bisogna capire come mappare un microservizio in una build CI.

Sam Newman all’interno del suo libro ci presenta tre opzioni:

- avere un unico repository in cui caricare tutto il codice ed effettuare una singola build per tutto il codice, quindi un’unica CI build per tutti i

(43)

32

microservizi, ciò comporta una semplificazione del sistema di CI ma ad ogni build verranno testati anche microservizi o porzioni di codice che non lo

necessitano e ciò costituisce un costo superfluo in termini di tempo, inoltre nel caso di un fallimento della build cercare l’errore su tutto il codice sarà più complicato, infine perdiamo la possibilità di avere microservizi autonomi anche nella fase di deploy e ciò può portare ad una riduzione dei rilasci in attesa di accumulare le build nel tempo e, perciò, ad avere un sistema meno dinamico;

- una seconda opzione è avere sempre un singolo repository ma non buildare tutto il codice bensì avere un albero con un’unica sorgente da cui si diramano i blocchi di codice, in questo modo verrebbe buildata la CI del codice

interessato, da un lato resta tutto semplificato con la presenza di un unico repository di cui preoccuparsi, dall’altra si potrebbe comunque cadere

nell’abitudine di buildare e validare più servizi alla volta e quindi non il singolo microservizio, perdendo anche in questo caso dinamicità;

- la terza soluzione proposta è quella consigliata, consiste nell’avere una singola CI build per ogni microservizio, in questo modo ogni microservizio ha il proprio repository con il codice sorgente e la propria build, il che mantiene l’indipendenza del microservizio e permette di validare e testare un

microservizio alla volta, quindi avremo un singolo artifact da deployare.

Quest’ultima soluzione permette anche di avere una maggiore autonomia a livello di organizzazione dei team che lavorano sul codice, in quanto non c’è la difficoltà di dover capire chi effettua la build su tutto il codice, perché è chiaro che ogni singola build verrà effettuata dal team che ha lavorato su quel

microservizio e nel caso di grandi progetti è importante.

Quindi la soluzione migliore è avere un singolo repository per ogni microservizio e, quindi, una CI build associata ad esso.

Un argomento vicino al concetto di Continuous Integration è quello del Continuous Delivery. Nel momento in cui utilizziamo l’integrazione continua di microservizi abbiamo visto che ogni microservizio va buildato e validato, per effettuare una build abbiamo varie fasi e vari test vengono applicati per validare il microservizio. È possibile avere test veloci ed altri più lenti e che richiedono più tempo, se tutti i test venissero effettuati insieme e senza un ordine logico, potremmo cadere nel caso in cui il risultato di un test fallito ci arrivi dopo molto tempo, per questo è meglio effettuare prima i test più rapidi e, se validi, allora procedere con quelli più pesanti che

richiedono tempi maggiori. Bisogna quindi creare una pipeline della build, in cui avere più step di verifica da passare, quindi solo se tutti i test hanno un risultato positivo si procede con la build del microservizio.

Questo concetto di pipeline ci permette di tenere traccia del progresso del nostro software nei vari step, permettendoci di osservare la qualità dello stesso. La creazione

Riferimenti

Documenti correlati

Lo stesso accade nella procedura di conferimento, dove i dati dei file iso e zip (che verranno descritti al termine di questa spiegazione preliminare ) sono cifrati con la

Le applicazioni in Angular presentano già un modulo iniziale chiamato Ap- pModule che è il contenitore iniziale nel quale possono essere inclusi tutti i componenti, gli altri moduli,

In this chapter, a series of dinuclear zinc-N-heterocyclic carbenes complexes (10-12) differently substituted on the nitrogen atoms of the NHC ligands were investigated in

Con Web.UP diventa semplice costruire siti Internet il cui contenuto rispecchia in tempo reale lo stato delle informazioni all'interno del gestionale di riferimento, Sme.UP, o

I controller sono i componenti responsabili della gestione delle richieste da parte dei client, Nel caso di questo microservizio di back-end, le risposte non sono pagine web, ma

[r]

ben definite da apparire quasi rappresentazioni della realtà, ma questo è nulla poiché è sufficiente sfiorare tale oggetto con un dito per innescare un meccanismo quasi diabolico

Tra le suture continue la più utilizzata per la sutura della cute è la continua a sopraggito semplice, questa consiste in una lunga serie di punti semplici in continua.. E' di rapida