Najvažnije iz članka
- Micro-frontend arhitektura, s Webpack Module Federation, omogućuje skalabilnost i neovisnost timova u razvoju velikih web aplikacija.
- Module Federation omogućuje dinamičko dijeljenje modula (komponenti, funkcija) i ovisnosti (React, Vue) u runtime-u, smanjujući veličinu bundle-a i poboljšavajući performanse.
- Ključni izazovi u produkciji uključuju upravljanje dijeljenim ovisnostima, komunikaciju između micro-frontenda, rukovanje greškama i optimizaciju performansi.
- Uspješna implementacija zahtijeva strogo verzioniranje, centralizirano upravljanje ovisnostima, korištenje Error Boundaries, lazy loadinga, te robusnu CI/CD i strategiju testiranja.
Sadržaj članka
- Što je Micro-frontend Arhitektura?
- Webpack Module Federation: Revolucija u Dijeljenju Koda
- Iskustva Iz Produkcije: Izazovi i Rješenja
- 1. Upravljanje Dijeljenim Ovisnostima (Shared Dependencies)
- 2. Dijeljenje Stanja i Komunikacija Između Micro-Frontenda
- 3. Upravljanje Greškama i Robusnost
- 4. Performanse i Optimizacija Bundle-a
- 5. Deployment i CI/CD Strategija
- 6. Testiranje i Kvaliteta Koda
- Zaključak
Suvremeni razvoj web aplikacija suočen je s izazovima sve veće složenosti, veličine timova i brzine isporuke. Monolitne frontend aplikacije, iako jednostavne za početak, brzo postaju usko grlo u velikim organizacijama. Upravo ovdje na scenu stupa micro-frontend arhitektura, obećavajući skalabilnost, neovisnost timova i bržu iteraciju. Jedna od najrevolucionarnijih tehnologija u tom području je Webpack Module Federation. Kroz ovaj članak, istražit ćemo kako Module Federation funkcionira, kakva su naša iskustva s njegovom implementacijom u velikim, produkcijskim sustavima te koje lekcije smo naučili.
Što je Micro-frontend Arhitektura?
Micro-frontend arhitektura je pristup izgradnji web aplikacija gdje se frontend razbija na male, neovisne i samostalne aplikacije koje se mogu razvijati, implementirati i testirati neovisno. Svaka od tih malih aplikacija, ili "micro-frontenda", može biti razvijena od strane zasebnog tima koristeći vlastitu tehnologiju (različiti framework-i, jezici, verzije), a zatim se kombiniraju u jedinstveno korisničko iskustvo. Ključne prednosti uključuju:
- Neovisnost Tima: Timovi mogu raditi na svojim dijelovima aplikacije bez značajnih ovisnosti o drugim timovima.
- Tehnološka Neovisnost: Omogućuje korištenje različitih tehnoloških stackova za različite dijelove aplikacije, što olakšava migraciju ili uvođenje novih tehnologija.
- Skalabilnost: Veće aplikacije mogu se lakše skalirati jer se problemi rješavaju na izoliranim komponentama.
- Manji Kod Baze: Svaki micro-frontend ima manju kodnu bazu, što olakšava razumijevanje, održavanje i testiranje.
- Brža Implementacija: Neovisne implementacije smanjuju rizik cijele aplikacije i omogućuju češće izmjene.
Tradicionalni pristupi implementaciji micro-frontenda često uključuju iframes, server-side includes, ili runtime integraciju putem alata poput Single-SPA. Iako funkcionalni, ovi pristupi dolaze s vlastitim skupom kompromisa, od performansi do kompleksnosti dijeljenja stanja (state sharing) i ovisnosti.
Webpack Module Federation: Revolucija u Dijeljenju Koda
Webpack 5 je donio Module Federation, ugrađenu značajku koja mijenja paradigmu dijeljenja koda među aplikacijama u vrijeme kompilacije (build-time). Umjesto da se dijeli statične biblioteke ili komponente, Module Federation omogućuje aplikacijama da dinamički uvoze module (komponente, funkcije, hooks, data store-ove) iz drugih Webpack buildova na runtime. Ovo je ključna razlika koja postavlja Module Federation ispred drugih rješenja.
Radi na principu remotes i hosts (ili containers). Host aplikacija uvozi module iz remote aplikacija. Remote aplikacije izlažu svoje module host aplikacijama. Ključne karakteristike:
- Dijeljenje Ovisnosti: Module Federation omogućuje automatsko dijeljenje ovisnosti (npr. React, Vue, Lodash). Ako host i remote koriste istu verziju neke biblioteke, ona se učitava samo jednom, značajno smanjujući veličinu bundle-a i poboljšavajući performanse.
- Dinamičko Učitavanje: Moduli se učitavaju dinamički na zahtjev, što znači da korisnik preuzima samo ono što mu je potrebno.
- Runtime Integracija: Integracija se događa u pregledniku, bez potrebe za kompliciranim server-side renderingom za spajanje različitih aplikacija.
- Agostičnost prema Frameworku: Iako je najčešće korišten s Reactom i Vueom, Module Federation je agostičan prema JavaScript frameworku.
// webpack.config.js - Host aplikacija
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... ostala konfiguracija
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};
// webpack.config.js - Remote aplikacija
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... ostala konfiguracija
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Widget': './src/Widget.jsx',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})
],
};
U ovom primjeru, hostApp može uvesti Widget iz remoteApp jednostavnim import Widget from 'remoteApp/Widget';.
Iskustva Iz Produkcije: Izazovi i Rješenja
Naš tim se suočio s izazovom upravljanja velikim brojem frontend aplikacija koje su se razvijale neovisno unutar monorepo strukture. Prelazak na Module Federation bio je ključan korak prema istinskoj micro-frontend arhitekturi. Evo nekih ključnih iskustava:
1. Upravljanje Dijeljenim Ovisnostima (Shared Dependencies)
Izazov: Najveća snaga Module Federationa, dijeljenje ovisnosti, ujedno je i izvor najvećih glavobolja. Različite verzije iste biblioteke (npr. React 17 vs. React 18) mogu dovesti do runtime grešaka. Konfiguracija shared opcije u Webpacku zahtijeva pažljivo planiranje i koordinaciju.
Rješenje:
- Strogo Definiranje Verzija: Koristili smo
requiredVersionistrictVersionopcije unutarsharedkonfiguracije kako bismo osigurali da se koriste kompatibilne verzije.singleton: trueje bio ključan za biblioteke poput Reacta, gdje je potrebno samo jedno učitavanje u cijeloj aplikaciji. - Centralizirano Upravljanje: U monorepo okruženju, definirali smo osnovne dijeljene ovisnosti i njihove verzije u
package.jsonroot-a i primjenjivali ih konzistentno kroz sve micro-frontende. Alati poputLernailiNxpomogli su u sinkronizaciji. - Testiranje Kompatibilnosti: Razvili smo end-to-end testove koji su provjeravali interoperabilnost micro-frontenda s različitim verzijama dijeljenih ovisnosti.
2. Dijeljenje Stanja i Komunikacija Između Micro-Frontenda
Izazov: Kako dijeliti stanje (state) i omogućiti komunikaciju između neovisnih micro-frontenda, a da se pritom ne stvara čvrsta sprega (tight coupling)?
Rješenje:
- Globalni Event Bus: Implementirali smo jednostavan globalni event bus (koristeći npr.
mittilieventemitter3) kao kanali za asinkronu komunikaciju. Ovo omogućuje micro-frontendima da šalju i primaju poruke bez direktnog poznavanja jedni drugih. - Kontekstualni Podaci (Context Providers): Za globalne podatke koji se ne mijenjaju često (npr. korisničke informacije, tema), koristili smo React Context API, izložen putem Module Federationa. Host aplikacija bi pružala kontekst, a remote aplikacije bi ga konzumirale.
- URL Parametri/Local Storage: Za jednostavnije dijeljenje stanja, URL parametri ili
localStorage/sessionStoragesu korišteni za prijenos manjih količina podataka.
3. Upravljanje Greškama i Robusnost
Izazov: Što se događa kada jedan remote micro-frontend prestane raditi ili se ne učita ispravno? Cijela aplikacija ne smije pasti.
Rješenje:
- Error Boundaries: Korištenje React Error Boundaries je ključno. Svaki micro-frontend treba biti omotan unutar vlastite Error Boundary kako bi se izolirali problemi. Ako micro-frontend padne, prikazuje se fallback UI, ali ostatak aplikacije nastavlja raditi.
- Lazy Loading i Suspense: Svi micro-frontendi su dinamički učitani koristeći
React.lazy()iSuspense. Ovo osigurava da se kod učitava samo kada je potreban i pruža fallback UI tijekom učitavanja. - Praćenje Performansi i Grešaka: Integracija s alatima za praćenje performansi (npr. Sentry, Datadog) bila je ključna za brzo identificiranje i rješavanje problema u produkciji.
4. Performanse i Optimizacija Bundle-a
Izazov: Iako Module Federation pomaže u dijeljenju ovisnosti, ako se ne konfigurira pravilno, može dovesti do prevelikih bundle-a i sporog učitavanja.
Rješenje:
- Aktivna Optimizacija
shared: Kontinuirano smo pratili veličinu bundle-a i fino podešavalisharedkonfiguraciju kako bismo osigurali da se samo najvažnije i najčešće korištene biblioteke dijele kaosingleton. - Code Splitting: Osim
React.lazy(), koristili smo i druge Webpack funkcionalnosti za code splitting kako bi se kod učitavao samo kada je to potrebno. - CDN Implementacija: Svi micro-frontendi i njihovi
remoteEntry.jsfajlovi su bili hostani na CDN-u radi bržeg globalnog pristupa.
5. Deployment i CI/CD Strategija
Izazov: Koordinacija deploymenta neovisnih micro-frontenda tako da uvijek rade zajedno u produkciji.
Rješenje:
- Nezavisni CI/CD Pipelini: Svaki micro-frontend imao je svoj neovisni CI/CD pipeline. To je omogućilo timovima da implementiraju promjene neovisno.
- Verzioniranje RemoteEntry.js: Ključno je bilo verzioniranje
remoteEntry.jsdatoteka. Kada se micro-frontend implementira, njegovremoteEntry.jsURL trebao bi se promijeniti (npr.remoteApp@http://cdn.example.com/remoteApp/v2.1.0/remoteEntry.js). Host aplikacija tada može referencirati specifičnu verziju ili se dinamički ažurirati. - Atomic Deployments: Koristili smo strategije atomic deploymenta kako bismo osigurali da se uvijek implementira cijela nova verzija, a da se ne miješaju stari i novi kod.
6. Testiranje i Kvaliteta Koda
Izazov: Osigurati kvalitetu i stabilnost u sustavu sastavljenom od mnogih neovisnih dijelova.
Rješenje:
- Komponentni Testovi: Svaki micro-frontend imao je opsežne unit i komponentne testove.
- Integracijski Testovi: Ključno je bilo imati integracijske testove koji simuliraju interakciju između hosta i remote-a.
- End-to-End Testovi (E2E): Selenium ili Playwright su korišteni za E2E testove koji su provjeravali cjelokupnu funkcionalnost aplikacije s učitanim svim micro-frontendima. Ovo je bilo ključno za uloviti probleme s dijeljenim ovisnostima i komunikacijom.
Zaključak
Module Federation je donio značajan pomak u načinu na koji gradimo velike web aplikacije. Omogućio nam je postizanje prave neovisnosti timova, tehničke slobode i agilnosti koja je bila nemoguća s monolitnim frontendima. Iako implementacija nije bez izazova, s pravilnim planiranjem, disciplinom u upravljanju ovisnostima, robustnom strategijom dijeljenja stanja i sveobuhvatnim testiranjem, prednosti su daleko veće od nedostataka. Za organizacije koje se bore s kompleksnošću i skalabilnošću frontend razvoja, Module Federation predstavlja iznimno moćan alat koji, kada se pravilno primijeni, može transformirati način na koji isporučujemo softver. Putovanje je bilo izazovno, ali krajnji rezultat je visoko skalabilna, održiva i agilna arhitektura koja nas je pripremila za buduće izazove modernog web razvoja.
Komentari