TSM - Drumul de la Monolit la Microservicii în lumea SaaS

Bogdan Nădișan - Digital Technology Architect @ Accenture


În ultimii șase ani, împreună cu o echipă foarte bună de programatori, am lucrat la realizarea unei noi aplicaţii Enterprise. La momentul începerii proiectului, tehnologia disponibilă era destul de prematură, astfel încât am avut la dispoziţie doar variante alpha şi beta a mai multor librării (MVC5, WebAPI2, Entity Framework 6, KnockoutJS, jQuery, kendoUI). Şase ani mai târziu, tehnologia s-a maturizat. Acum avem pe piaţă o aplicaţie Enterprise, care a fost implementată cu succes la mai mulți clienţi din diferite părţi ale lumii. Recent, clientul care vinde acest produs a făcut o pivotare a modelului de business spre SaaS.

În acest articol nu voi încerca să expun avantajele şi dezavantajele unui monolit, respectiv ale unei arhitecturi bazate pe microservicii, ci mai degrabă doresc să prezint rezultatele cercetării făcute de noi pentru trecerea de la o arhitectură monolit la una bazată pe microservicii, având în vedere un model de business SaaS, cu deployment în cloud (AWS, mai exact).

Contextul

Aplicaţia are o arhitectură pe mai multe nivele. Acestea sunt DataLayer, BusinessLayer şi ServiceLayer, iar deasupra ServiceLayer-ului este o aplicaţie HTML5, construită ca o colecţie de SPA-uri (Single Page Application).

Toate nivelele aplicaţiei care sunt sub layerul de HTML5 şi deasupra RDB-ului rulează în acelaşi proces, ceea ce face ca aplicaţia să ruleze ca un monolit.

Schema este simplă, însă pentru a înţelege mai bine magnitudinea proiectului, găsiţi în continuare mai multe detalii despre conţinut:

Statisticile de mai sus au fost extrase din ultima rulare SonarQube, din pipeline-ul de Continuous Integration al proiectului.

Figura 1 - Arhitectura multinivel

Provocarea

Avem o aplicaţie care pune la dispoziţie o varietate de functionalităţi ce pot fi folosite în integrare cu terţe aplicaţii sau chiar pentru aplicaţii nou create.

Dacă dorim să expunem funcţionalităţile într-un business model SaaS, atunci ar trebui să luăm în considerare diverse aspecte tehnice, cum ar fi: "Costs of deployment", "Data Governance/Data segregation", "Multi tenancy", "Privacy", "Compliance", "Security", "Availability" and "Technology".

Soluţia

Microservicii

Termenul de microservicii este deja un buzz-word bine cunoscut, iar în domeniul nostru este un termen în jurul căruia s-au pornit multe dezbateri. Fără să intru într-o asemenea dezbatere şi fără să pornesc discuţia definirii termenului de microservicii, iată câteva provocări cu care ne-am întâlnit concret şi pe care am dorit să le rezolvăm folosind acest concept:

  1. La fiecare start-up al unui monolit, acesta consumă multe resurse, chiar dacă procesul e latent. În cazul în care am vrea să scalăm o anumită capabilitate a monolitului, trebuie să scalăm de fapt, tot monolitul, cu toate funcţionalităţile sale, ceea ce crește preţul deploymentului pentru clienţi.

  2. Restricţii privind mediul de deployment în care se rulează aplicaţiile. În cazul nostru, folosind tehnologii .NET, suntem mai mult sau mai puţin dependenţi de Windows OS.

  3. Mărimea aplicaţiei poate încetini procesul de pornire (Cold Start-up).

  4. La fiecare versiune la care facem release, e nevoie să facem redeploy la tot monolitul, ceea ce implică mai multe riscuri.

  5. Complexitatea codului şi a funcţionalităţii.

  6. Un singur technology stack de development. Vrem să luăm în considerare chiar şi folosirea altor tehnologii care sunt foarte potrivite pentru implementarea anumitor capabilităţi.

  7. Fiabilitate (Memory Leaks/Exceptiile netratate au un impact mai mare asupra aplicaţiilor de tip monolit).

Decuplează capabilităţi, nu cod

Aşadar, cum trecem de la o arhitectură monolit la o abordare bazată pe microservicii?

Încearcă să te uiţi la monolitul la care lucrezi şi gândeşte-te la ce capabilităţi oferă. Odată ce le vei înţelege în profunzime, ia în considerare extragerea codului ce face referire la una dintre aceste capabilităţi sau chiar consideră rescrierea acesteia de la zero. Dacă ai în perspectivă rescrierea, atunci ai opţiunea de a alege tehnologia care se potriveşte cel mai bine cu acea capabilitate specifică.

Porneşte de la separarea capabilităţilor simple şi separate deja; astfel poţi să exersezi acest proces pentru a-l aplica şi pe capabilităţile mai complexe.

Capabilitatea de autentificare este un exemplu de capabilitate marginală, ceea ce o face, în teorie, mai uşor de separat.

Începe cu "Macroservicii"

Extrage "macroservicii" în prima instanţă. Este mai important să extragi o capabilitate specifică ce aduce valoare produsului decât să extragi doar cod. Atenția trebuie orientată către capabilităţi, nu pe cod; codul e doar un tool care produce capabilităţi.

De exemplu, în procesul de decuplare a capabilităţilor unui sistem de ECommerce, programatorii ar putea porni de la extragerea unui singur serviciu de tip "buy", în care să înglobeze atât conţinutul coşului de cumpărături, cât şi capabilitatea de cumpărare ("check-out"). Acest serviciu poate să fie considerat "macroserviciu" în ideea în care din capabilitatea de mai sus se poate extrage ulterior o capabilitate de "check-out". Aceşti paşi sunt benefici procesului de a sparge un monolit în bucăţi mai mici.

"Slice"-urile capabilităţilor

Când separi capabilităţile pe "slice"-uri, încearcă să o faci vertical. Dacă e posibil, încearcă să atingi toate layerurile monolitului.

De exemplu:

Figura 2 - Arhitectura multinivel împreună cu verticalele tehnice

În imaginea de mai sus, pe partea dreaptă, se observă cum toate nivelele au acum un "slice" vertical, bazat pe capabilităţi specifice (reprezentate în diferite culori: fiecare culoare reprezintă o capabilitate), ce pot fi transformate în noi aplicaţii, rezultând noi microservicii. Fiecare capabilitate poate avea amprente diferite pe fiecare nivel. De aceea, efortul de a extrage o anume capabilitate diferă în funcţie de impactul pe care îl are asupra monolitului.

Cum se face separarea

Paşii de urmat pentru separarea unei noi capabilităţi:

Tehnologii folosite

Într-o arhitectură de tip monolit, când construieşti o nouă capabilitate, tehnologia pe care o vei folosi este în general constrânsă de tehnologia în care a fost scris monolitul. De exemplu, dacă construieşti o nouă capabilitate într-un monolit scris în .NET, va trebui să te limitezi la .NET; acelaşi lucru se aplică la Java sau alte tehnologii.

Într-un mediu bazat pe microservicii, atunci când construieşti o nouă capabilitate, ai puterea şi flexibilitatea să alegi tehnologia care se potriveşte capabilităţii ce urmează să o creezi.

Figura 3 - Arhitectura multinivel împreună cu verticalele tehnice și funcționale

Chiar dacă am separat trei capabilităţi din monolit, drumul până la un ecosistem bazat doar pe microservicii continuă. Faptul că ai monoliţi mai mici sau "macroservicii" nu înseamnă că drumul e greşit; orice "slice" reuşeşti să scoţi din monolit, e un pas în faţă.

Însă, să nu uităm că, pentru a putea vinde produsul la care lucrăm în continuare, trebuie să livrăm noi funcţionalităţi pe piaţă în timp record. Totdeauna trebuie să încercăm să punem în balanţă efortul de a livra rapid cu timpul investit în livrarea capabilităţii ca un microserviciu.

AWS

Înainte de a separa monolitul în bucăţi mai mici - microservicii, în cazul în care ştii că urmează să faci deployment pe o platformă cloud, e indicat să investighezi ce servicii sunt deja disponibile pe acea platformă pe care vei lansa produsul sau capabilitatea implementată.

Acest lucru este necesar deoarece platformele cloud pot avea servicii gata de folosit şi nu mai este necesar să le construieşti tu.

Desigur că trebuie să fii atent atunci când alegi să foloseşti un serviciu al unei platforme și vei deveni "tightly-coupled" cu acea platformă. Dacă planifici să suporţi diverse platforme could (Azure/AWS/Alibaba), va trebui să iei în considerare construcţia unui nivel de abstractizare deasupra serviciilor cloud pe care plănuieşti să le foloseşti.

Mai jos, este un exemplu de deployment a trei noi capabilităţi (reprezentate în diferite culori - în secțiunea de private subnet) ce au fost extrase din monolit, împreună cu aplicaţia monolit.

Figura 4 - Scenariu de deployment AWS multiregiune

Capabilităţile pot rula fie în instanţe de EC2, fie Serverless ca funcţii lambda. În diagrama de mai sus, acestea sunt deployed ca funcţii lambda.

Iată câteva dintre serviciile AWS pe care considerăm să le folosim:

Orchestrarea

Să presupunem că avem o colecţie de microservicii care ar trebui executate într-o ordine specifică şi configurabilă, cu un anumit nivel de atomicitate şi cu un nivel ridicat de interacţiune între acestea.

Pentru această orchestrare, am luat în considerare aplicaţii ce pot invoca servicii REST şi SOAP într-un mod configurabil. În cazul nostru, am decis să folosim Mendix.

Cu Mendix poţi crea ceea ce ei numesc "microflows". Un "microflow" îţi permite să exprimi schema logică a aplicaţiei SaaS. Acest microflow poate efectua acţiuni precum invocarea serviciilor REST şi SOAP într-un mod visual şi configurabil, fără a fi nevoie să scrii cod. Pe lângă invocări de servicii, ai posibilitatea să creezi entităţi şi view-uri pe care le poţi folosi pentru a face diferite transformări care sunt în general necesare pentru a îndeplini contractele microserviciilor. Trebuie să repet că funcţionalităţile de mai sus se pot face doar prin configurare.

Un exemplu de orchestrare prin configurare în Mendix se poate vedea în Figura 5.

Figura 5 - Exemplu de Mendix Microflow

Concluzie

Construirea unui produs SaaS pentru aplicaţii Enterprise este o încercare complexă. SaaS de abia a început să pătrundă în lumea Enterprise, în principal pentru aplicaţii noi. Adoptarea SaaS este condusă, evident, de business. Cu toate acestea, credem că în timp, mai multe aplicaţii "legacy" vor fi înlocuite de către SaaS, iar această tranziţie va fi accelerată odată ce produsele Enterprise SaaS se vor maturiza şi vor îndeplini cerinţele reale Enterprise.