ABONAMENTE VIDEO REDACȚIA
RO
EN
Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 129
Abonament PDF

Integrarea Run-Time în arhitectura cu microfrontenduri: o nouă viziune a aplicațiilor web

Andrei Miron
Frontend Technical Lead @ Accesa



PROGRAMARE


Ca în orice domeniu din ziua de astăzi, cu precădere în industria IT, trebuie să privim spre viitor şi să găsim moduri fezabile de a ne alinia nevoilor societății şi tehnologiei care sunt în continuă schimbare. Fie că vorbim despre frameworkuri, tooluri sau procese, deznodământul este același: tehnologiile noi le vor înlocui pe cele vechi. Acest lucru este valabil și în Web Development, unde cele mai mari modificări au fost la frameworkurile pe care le foloseam, la cum să redăm în DOM mai eficient o pagină, cum să facem modelarea datelor mai necostisitoare din punctul de vedere al memoriei și așa mai departe. Se pare că, în sfârșit, a venit momentul să ne concentrăm și asupra modului cum alegem să facem arhitectura unui proiect, în așa fel încât să putem dezvolta produse mai rapid, în echipe mai numeroase într-un mod independent. Aici intră în discuție Microfrontends.

De ce Microfrontends și care sunt beneficiile aduse

Microfrontendurile sunt un concept relativ nou în lumea dezvoltării web, dar au câștigat rapid popularitate datorită numeroaselor beneficii. Pe măsură ce dezvoltatorii au început să descompună aplicațiile monolitice în servicii mai mici, independente, ei și-au dat seama că aceeași abordare ar putea fi aplicată și pentru frontendul aplicațiilor web. Astăzi, există numeroase instrumente și cadre disponibile pentru construirea de micro frontenduri, făcându-le mai ușor pentru dezvoltatori să adopte această abordare în proiectele lor.

Nevoia de micro frontenduri apare din cauza limitărilor aplicațiilor monolitice. Prin decentralizarea frontendului în bucăți mai mici de sine stătătoare, putem depăși acest lucru, păstrând în continuare structura de monorepo. Pe scurt, aceasta presupune dezvoltarea unor aplicații mai mici (module) care pot fi rulate independent una de cealaltă, care, la final, se vor integra pentru a forma o aplicație completă. Motivele pentru o astfel de abordare sunt numeroase:

  1. Scalabilitatea. Putem avea mai multe echipe care lucrează la diferite părți din aplicație fără să existe conflicte. Fiecare își poate gestiona modul în care își va lansa aplicația. În cazul unei complexități mărite a unei componente, se poate interveni doar asupra scalabilității acelei componente fără a avea impact asupra restului aplicației.

  2. Flexibilitatea tehnologiilor folosite. Pentru că fiecare micro frontend este independent, avem posibilitatea de a folosi tehnologii și frameworkuri diferite pentru fiecare dintre acestea, atâta timp cât vor fi respectate câteva reguli. Aceasta ajuta la formarea mai rapidă a echipelor, deoarece o aplicație nu va mai fi constrânsă doar de un singur framework. Totodată, oricând un nou micro frontend va trebui integrat, se poate alege cea mai nouă tehnologie.

  3. Construirea aplicațiilor în mod independent. Posibilitatea ca fiecare micro frontend să fie construit independent, fără a fi nevoie de a reconstrui întreaga aplicație integrată.

  4. Ușurarea procesului de depanare. Având aplicația structurată în micro frontenduri, riscul ca o problemă de la un micro frontend să afecteze un altul este foarte mic. Aceasta face ca diagnosticarea problemei să fie mai restrânsă și toată aplicația să fie mai stabilă.

Microfrontenduri cu Module Federation

Cel mai probabil, la un moment dat, fiecare dintre noi am interacționat cu acest concept, însă fără să ne dăm seama. Un astfel de exemplu pot fi unele pachete (componente) publicate într-un Artifactory (NPM) pe care le importăm într-unul sau mai multe proiecte, pentru a reduce duplicitatea codului. Pornind de aici, putem distinge trei moduri prin care putem integra componentele în aplicații:

  1. Build-Time Integration. Înainte ca pagina web să se încarce, fișierele aduse din afara aplicației, trebuie deja încărcate și inițializate (pachete NPM);

  2. Server-Side Integration. Un server va decide ce componente trebuie încărcate în aplicație.

  3. Run-Time Integration. Componentele vor fi aduse și încărcate în pagina web, doar în momentul în care trebuie redate în DOM.

Dacă primele două integrări sunt cât de cât cunoscute, noutatea vine la Run-Time Integration. Astfel ne vom concentra asupra acesteia. Aici diferența este că micro frontedurile, care pot fi și pagini de sine stătătoare, sunt integrate în aplicație print-un URL specific (ex: https://my-app.com/MFE/remoteEntry.js), fără a fi nevoie de o reconstruire a aplicației container, atunci când o modificare va fi adusă micro frontendului copil.

Deși avem o arhitectură de micro frontenduri, ideal ar fi să existe, în continuare, o componentă principală (container/părinte/shell), care va trebui să le integreze pe celelalte (copii). Doar prin intermediul acesteia, în cazuri speciale, micro frontendurile să poată comunica între ele.

O dată cu această structurare vine și un set de recomandări, care ajută pe măsură ce aplicațiile vor crește:

Cum acest concept de integrare al micro frontendurilor este cel mai nou, la fel sunt și instrumentele care ne ajută în dezvoltarea cât mai ușoară a acestor aplicații, și de aici face parte și ModuleFederation.

Module Federation este un plugin integrat în Webpack 5, care permite micro frontendurilor să interschimbe între ele componente, dependințe sau chiar aplicații întregi, fără a fi nevoie de a copia codul în proiecte separate. Înainte, dacă aveam mai multe micro frontenduri pe care voiam să le utilizam în alte aplicații, acestea trebuiau să fie construite și implementate în interiorul aplicațiilor pe care le foloseau, astfel ajungea ca performanța și dimensiunea aplicațiilor să crească exponențial, datorită duplicității.

Acest plugin vine ca alternativă, deoarece permite micro frontendurilor să distribuie codul într-un mod mult mai eficient prin încărcarea resurselor în mod dinamic, doar atunci când o pagină web este redată în DOM (Run-Time), și nu când este construită pentru a fi implementată. Orice modificare adusă unui micro frontend copil, se va propaga automat la container și la celelalte micro frontenduri fără a fi nevoie de implementări separate pentru fiecare aplicație în parte. Astfel, codul devine mai ușor de menținut și de înțeles, în timp ce echipele pot lucra din ce în ce mai independent.

Configurarea și utilizarea Module Federationului este destul de simplă. Dar, pe măsura ce aplicația crește, la fel va crește și complexitatea, iar pentru un start de bază este destul să avem următoarele elemente:

În primul rând, pentru a putea expune și a avea acces la diferite micro frontenduri, vom avea un fișier numit "webpack.config.js" în ambele aplicații, dar cu configurări diferite:

În Microfrontendul Dashboard (copil):

În Microfrontendul Container (părinte):

În continuare, vom lucra cu următoarea structură de fișiere. Deoarece dorim să avem aplicații independente de un limbaj de programare, vom implementa și o funcție care inserează conținutul returnat din micro frontendurile copii în elementele din containerul părinte. Pentru exemplul ales mai departe, vom folosi ReactJs.

Un lucru important de reținut este că folosim un mod dinamic de a consuma micro frontendurile. Prin urmare, chiar dacă folosim componente din același repository (monorepo), trebuie să încărcăm în mod asycron aceste componente, deoarece ele vor fi interpretate ca module externe. De aceea, trebuie să avem un fișier de 'pornire' (bootsratp.js) unde să copiem conținutul fișierului 'index.js' și apoi să importăm 'bootstrap.js' în 'index.js'.

Ulterior, ne vom concentra pe aplicația Dashboard, unde tot ce trebuie să facem este să modificăm 'bootstrap.js', pentru a crea funcția care să redea în DOM conținutul din Dashboard în aplicația părinte.

/dashboard/src/bootstrap.js

import React from 'react';
import ReactDOM from 'react-dom';

const renderDashboard = (element) => {
    ReactDOM.render(, element);
}

export { renderDashboard };

Acum putem trece la aplicația Container, unde primul constă în a aduce componenta de Dashboard și a o reda în DOM. Vom face asta cu ajutorul unui ref și apelând funcția expusă cu elementul în care vom încărca conținutul din Dashboard.

/container/src/DashboardComponent.js
import { renderDashboard } from 
  'dashboard/DashboardPage'

import React, { useRef, useEffect } from 'react';

export default () => {
  const ref = useRef(null);

  useEffect(() => {
    renderDashboard(ref.current);
  }, []);

  return 
; };

Iar de aici setupul va fi la fel ca într-o aplicație de ReactJs normală, având următoarele fișiere:

În acest moment, vom avea o aplicație care va folosi micro frontenduri pentru a randa conținutul. Este tot ce trebuie pentru a ne putea extinde.

Ce putem face mai bine și unde trebuie să avem grijă?

Unul dintre cele mai importante lucruri care trebuie menționate când este vorba despre micro frontendurile îl reprezintă state managementul și, mai precis, cum se face schimbul de date între componente. Pentru asta avem câteva soluții:

Un alt lucru important este rutarea deoarece, pe lângă rutarea pe care o vom face în container, trebuie să ne ocupăm și de celelalte micro frontenduri. Desigur că putem să tratăm acest aspect și din container adăugând încă o cale pentru fiecare nouă rută din microfrontenduri, dar aceasta se pierde în independenţa aplicațiilor, fiind necesară o nouă integrare a containerului.

Un alt aspect la care trebuie să fim atenți este CSS-ul. De multe ori, unele stiluri se vor suprascrie deoarece se lucrează în echipe separate, fiind ușor ca o clasă sau un element dintr-o aplicație să suprascrie alta. Printre cele mai ușoare soluții se numără:

O mare îmbunătățire pe care putem să o aducem se axează pe dependințele care sunt împărtășite între module. După cum am observat în fișierul 'webpack.config', există o cheie 'shared' care are ca valoare un vector cu numele tuturor librăriilor folosite în acel micro frontend. Când aplicația Container importă şi folosește acest modul, se va verifica dacă acele dependințe sunt deja încărcate în interiorul aplicației și se vor refolosi.

Pentru că fiecare aplicație creşte în ritmul ei, va fi greu să ținem cont de toate librăriile utilizate și să le actualizăm manual peste tot. Așa că putem să automatizăm dependințele care vor fi aduse, pentru a nu fi duplicate astfel: 'shared:packageJson.dependencies'.

Orice lucru frumos are și o parte mai puțin bună

Din păcate, nimic nu poate veni doar cu avantaje. În cazul de față cel mai mare dezavantaj este complexitatea configurării aplicațiilor. Deoarece este un nou tip de integrare al arhitecturii, lucrurile avansează într-un ritm alert, iar resursele și soluțiile pentru a acoperi toate problemele care apar în timpul dezvoltării și cererilor businessului pot fi limitate sau complicat de implementat. Fie că vorbim despre tehnologia aleasă pentru fiecare micro frontend, despre crearea rutelor aplicației sau despre cum putem să stocăm și să transferăm datele dintr-un micro frontend în altul, la început, majoritatea lucrurilor vor fi personalizate pentru nevoile fiecărei aplicații. Însă o dată cu stabilizarea proiectului şi trecerea peste această etapă, beneficiile vor fi multiple. Pe de altă parte, nimic nu asigură că, alegând o altă arhitectură, nu vor apărea alte dificultăți.

O altă problemă se referă la erorile generate de micro frontenduri. Deși am spus că aceste micro frontenduri fac aplicațiile mai ușor de depanat, există și situații când unele erori pot fi descoperite doar la integrarea lor în componente părinte.

Alte contraargumente sunt greu de găsit deoarece, dacă reușim să trecem peste integrare, beneficiile care vor fi aduse, în cele mai multe cazuri, depășesc riscul adus și timpul petrecut pentru înțelegere a arhitecturii de către echipe.

Unde ne duce trendul

Fie că vrem sau nu, din ce în ce mai multe proiecte se vor structura în acest mod, chiar dacă este vorba despre soluții implementate la nivel de Build sau Run-Time. Faptul că proiectele sunt din ce în ce mai mixte și nevoia separării codului în componente cât mai mici este în creștere ne va determina să privim această opțiune drept cel mai bun mod de a merge înainte. În exemplul nostru am ales conceptul Run-Time pentru a implementa aceste micro frontenduri, dar acest lucru se poate face în multiple moduri și în multiple configurații. Fie că avem un singur monorepo sau repository-uri mai multe în diferite spaţii de lucru, fie că importăm micro frontendurile în timpul creării aplicației sau folosim soluții dinamice cu Module Federation, avantajele aduse vor fi din ce mai evidente pe măsură ce productivitatea şi autonomia echipelor va crește, iar complexitatea va scădea o dată cu apariția unor noi soluții.

LANSAREA NUMĂRULUI 149

Marți, 26 Octombrie, ora 18:00

sediul Cognizant

Facebook Meetup StreamEvent YouTube

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects