TSM - Ghidul tău rapid pentru arhitectura aplicațiilor Java

Alex Popescu - Senior IT Consultant @ msg systems Romania


Microservicii sau monoliți, arhitecturi serverless sau orientate pe servicii, MACH, blocuri fundamentale ale arhitecturii aplicațiilor Java, containerizare, aplicații 12 factor și multe altele - toate reprezintă soluții pe care le avem la dispoziție, însă niciuna care să răspundă tuturor provocărilor. Prin urmare apare întrebarea: "Contează cu adevărat tipul de arhitectură? Este o arhitectură bazată pe microservicii superioară uneia monolitice?"

În loc să ne concentrăm pe imaginea de ansamblu, propun examinarea detaliilor mărunte, deoarece știți că diavolul se află în detalii. Cred cu fermitate că, indiferent de arhitectura aleasă sau dacă sistemul tău este bazat pe cloud sau on-premise, succesul depinde de anumite aspecte cruciale ale aplicației tale. De aceea, implementează aceste componente eficient și vei crea un mediu în care aplicația ta poate înflori.

În acest articol îmi propun să împărtășesc aceste elemente arhitecturale eficiente și de succes.

Să începem! Primul lucru despre care vom discuta sunt opțiunile de framework. Cum acesta este un ghid rapid și nu un tutorial, voi încerca să vă prezint doar lucrurile esențiale.

Stiva tehnologică

Pentru aplicațiile tradiționale care înclină spre o structură monolitică, Jakarta EE este o alegere bună. Nu mai trebuie să îți faci griji cu privire la adăugarea funcționalităților sau la alegerea bibliotecilor și a tehnologiilor. Majoritatea funcțiilor de care ai nevoie sunt integrate în serverele de aplicații. Wildfly și Glassfish sunt o alegere excelentă.

Dacă preferi structuri ușoare sau microservicii, dar apreciezi totuși Jakarta EE, ia în considerare MicroProfile. Este un framework mai ușor cu funcții încorporate, precum verificările de sănătate, metricile și toleranța la erori. Deși este promovat pentru microservicii, este util și pentru alte tipuri.

Dacă Jakarta EE nu este pe gustul tău, încearcă Spring Boot. Este versatil, bogat în funcții și un competitor solid pentru Jakarta EE și MicroProfile împreună.

Pentru cei mai aventuroși, Quarkus merită explorat. Este rapid, ușor și orientat spre performanță, conceput pentru suport nativ, deși are unele dezavantaje, cum ar fi timpii de construcție mai lungi și eventuala incompatibilitate cu reflexia.

Articolul se concentrează pe aplicațiile backend și pe microservicii, iar subiectul UI va fi omis. Indiferent de tehnologia aleasă, există mai multe aspecte esențiale de reținut, pe care le vom discuta în continuare. Și reține că nu există o soluție perfectă - totul depinde de context și de nevoile tale.

Configurarea

Un framework eficient necesită un management robust al configurărilor. Deși ar putea părea banal, managementul configurărilor este de o importanță deosebită din două motive (evident mai multe însă menționez doar două):

Când proiectezi, ia în considerare cerințele potențiale de business, frecvența modificărilor, necesitatea repornirilor, interfața cu utilizatorul încorporată și, mai presus de toate, considerentele de securitate pentru stocarea datelor sensibile. Iată câteva îndrumări care vor ajuta la menținerea unui design robust:

Ia în considerare o soluție practică, precum Spring Cloud Config pentru o abordare concretă.

Serviciile REST

Explorarea serviciilor REST ar putea umple un articol întreg, dar iată câteva reguli simple, bune practici pentru un design coerent:

Tip Metoda HTTP Descriere
Create POST Crete a new entity (IN) The new entity
in request body (OUT) 201 + "Location"
header with entity URI (OUT) 400 +
Invalid entity error as response (OUT)
500 + Internal server error as response
Read GET Read entity by ID (IN) entity ID as path
param (OUT) 200 + The entity in the
response body (OUT) 404 + Entity not
found as response (OUT) 500 + Internal
server error as response
Query GET/POST Search for entities (IN) The search
criteria as query param or request body
(OUT) 200 + The search result as
response (OUT) 400 + Invalid search
criteria as response (OUT) 500 +
Internal server error as response
Update PUT Update an existing entity (IN) The id of
the entity as query param and entity to
update in the request body (OUT) 200 +
found entities as response body (OUT)
400 + Invalid entity error as response
(OUT) 404 + Entity not found as response
(OUT) 500 + Internal server error as
response
Delete DELETE Delete entity by ID (IN) Entity ID as
path param (OUT) 200 + Entity deleted
successfully (OUT) 404 + Entity not
found as response (OUT) 500 + Internal
server error as response

Securitatea

Simplifică securitatea aplicației tale cu OIDC și Oauth2, folosind un furnizor de identitate preferat, cum ar fi KeyCloak. Această combinație acoperă majoritatea fluxurilor/tipurilor de granturi necesare.

Dacă aplicația ta utilizează SAML și nu poate fi schimbată ușor, IDP-ul tău poate converti de obicei tokenul din bătrânul SAML în Oauth2 token.

Evită autentificarea "basic" și nu-ți crea niciodată propriul framework de securitate, cu excepția cazului în care ești expert în securitate.

Loggingul

Loggingul este vital și, fără îndoială, monitorizarea cu atât mai mult. Fără un logging coerent, poți pierde timp prețios vânând buguri, timp pe care l-ai putea petrece într-un bar sau cu familia ta. Respectă aceste reguli:

Pentru stack-ul tău:

Monitorizarea

Nu subestima funcțiile native de monitorizare ale frameworkului tău. Adnotările oferite de Microprofile, de exemplu, pot facilita monitorizarea ușoară a metricilor:

Astfel de adnotări sunt disponibile în diferite frameworkuri, inclusiv Spring. În loc să-ți creezi propriile metrici, profită de instrumentele existente ale frameworkului tău pentru a optimiza fluxul de lucru și pentru a standardiza procesele.

Trasabilitatea

Într-o aplicație monolitică, urmărirea apelurilor metodelor în scopul localizării zonelor lente este destul de simplă, deoarece toate acțiunile au loc într-un singur context. Adăugarea de măsurători pentru a culege date, cum ar fi timpii de obținere a datelor din baza de date, este relativ simplă. Cu toate acestea, într-un mediu de microservicii în care ele sunt interconectate, identificarea blocajelor devine o sarcină complexă.

Există două soluții obișnuite pentru a urmări apelurile de metode și interacțiunile dintre servicii: implementarea personalizată și utilizarea de API-uri.

Implementarea personalizată implică, de obicei, adăugarea unui trace ID în liniile de log, nume de servicii sau metode și, eventual, starea sau durata. Aceste loguri pot fi căutate folosind instrumente precum Splunk. Această abordare, deși simplă și realizabilă prin Spring Aspect sau JEE Interceptor, prezintă riscuri precum standardizarea și instrumente limitate de UI.

Abordarea API implică soluții precum OpenTracing și Jaeger, primul furnizând standardul și API-ul, iar cel de-al doilea oferind implementarea. Deși configurarea este mai complexă, poate fi utilă pentru proiecte în creștere și în ecosisteme mai mari.

Ambele metode îți permit să vizualizezi și să înțelegi interacțiunile din ecosistemul tău.

Reziliența

Reziliența în microservicii Java implică funcționalități cheie, cum ar fi întrerupătoarele de circuit, timeout-uri și failover .

Un întrerupător de circuit previne, într-un ecosistem de microservicii, eșecurile de serviciu în cascadă. MicroProfile oferă o adnotare @CircuitBreaker în acest scop. De exemplu:

 @CircuitBreaker(requestVolumeThreshold = 4,  
failureRatio = 0.75, delay = 1000)

public Connection serviceConnect() { ... } 

Acest fragment de cod activează întrerupătorul dacă 75% dintre solicitări eșuează într-o fereastră continuă de patru solicitări, după care așteaptă 1000 ms înainte de a reîncerca.

Timeout-urile împiedică sistemul să rămână blocat în operațiuni care au puține șanse de reușită. Acest lucru este realizabil folosind adnotarea @Timeout din MicroProfile.

Timeout(500)  
public Connection serviceConnect() { ... }

În acest exemplu, dacă metoda serviceConnect depășește 500 de milisecunde pentru a răspunde, aceasta este întreruptă automat.

Funcțiile de failover permit redirecționarea automată a cererilor de la un serviciu defect la unul funcțional.

Combinând aceste strategii, ne putem menține reziliența aplicației, putem gestiona și ne putem recupera cu eleganță după eșecuri.

Concluzii

Deși un design arhitectural poate oferi avantaje semnificative pentru proiectul tău, este ca și cum te-ai aștepta să câștigi o cursă doar pentru că ai o mașină nouă și strălucitoare!

Să luăm ca exemplu arhitectura MACH (bazată pe Microservicii, cu API-first, nativă în Cloud și Headless). În ciuda numelui său impresionant, a flexibilității și a scalabilității pe care le oferă, aceasta vine și cu provocări. Gestionarea mai multor microservicii poate deveni complexă. Aceasta necesită echipe tehnice competente, pricepute în sisteme bazate pe cloud, microservicii și API-uri. Menținerea consistenței datelor ar putea prezenta dificultăți din cauza bazelor de date separate pentru fiecare microserviciu. Eventuala latență de comunicare între servicii cauzată de rețea, modificările complexe ale relațiilor dintre servicii, precum și nevoia de securitate la nivel înalt, se adaugă, de asemenea, la provocări. În cele din urmă, costurile operaționale pot crește în funcție de dimensiunea proiectului și de furnizorul de servicii cloud selectat.

Principiile pe care le-am conturat aici sunt aplicabile universal, indiferent de arhitectură sau de mediu. Concentrându-te pe un design de API corespunzător, vei implementa o monitorizarea și trasabilitate eficientă. În acest fel îți vei consolida aplicația împotriva eșecurilor, vei deschide calea pentru o aplicație robustă și ușor de gestionat în producție. Concentrându-te pe aceste aspecte, vei garanta identificarea și rezolvarea rapidă a problemelor și vei asigura că sistemul tău poate face față provocărilor neașteptate fără perturbări semnificative.

Reține că acestea sunt doar puncte de pornire și că fiecare subiect pe care l-am abordat aici ar putea necesita propriul său articol.

În speranța că am subliniat suficient semnificația acestor subiecte, voi încheia cu o colecție de resurse pentru a-ți permite să le aprofundezi.

Gânduri de final