I micro frontend estendono l'idea dei microservizi allo strato di presentazione. Invece di costruire un'unica applicazione monolitica, il frontend viene scomposto in applicazioni più piccole e indipendenti, sviluppate, provate e rilasciate separatamente. Ogni micro frontend possiede un dominio di business distinto e può essere costruito con altre tecnologie, da altre squadre, con altri ritmi di rilascio.
L'architettura è nata dalle stesse pressioni che hanno spinto i microservizi lato server. Con il crescere della complessità, i frontend monolitici sono diventati difficili da mantenere, lenti da costruire e rischiosi da rilasciare. Una sola modifica in un punto qualsiasi del codice imponeva di ricostruire e ridistribuire l'intera applicazione, rallentando le squadre e creando attriti fra gruppi che volevano procedere a velocità diverse.
Che cosa sono i micro frontend?
Un'architettura a micro frontend divide un'applicazione web in fette funzionali, ciascuna di proprietà di una squadra autonoma. La squadra risponde di ogni strato della propria fetta: i componenti d'interfaccia, la logica di dominio, il recupero dei dati e l'integrazione con il server. L'utente vede un'applicazione unica e coerente, ma dietro le quinte essa è composta da più applicazioni piccole che girano nella stessa finestra del browser.
Questa scomposizione segue i principi della progettazione guidata dal dominio. Ogni micro frontend corrisponde a un contesto delimitato: un confine logico attorno a una capacità di business precisa. Il percorso di pagamento, il catalogo prodotti, il profilo utente e la ricerca possono essere micro frontend distinti, affidati a squadre distinte.
La differenza chiave rispetto alla semplice suddivisione del codice in moduli è che i micro frontend sono indipendenti al momento della costruzione e del rilascio. Ognuno ha la propria catena di build, il proprio repository, le proprie prove e il proprio calendario. È questa indipendenza a dare all'architettura i suoi vantaggi e a crearne le difficoltà.
Un micro frontend non è una libreria di componenti né un insieme di utilità condivise. È un'applicazione autonoma che viene composta a tempo di esecuzione dentro un'applicazione più grande. Questa distinzione è essenziale per capire tanto la forza quanto il costo dell'architettura.
Module Federation con Webpack 5
Module Federation è l'approccio più diffuso negli ecosistemi React e Angular. Introdotto con Webpack 5, consente a un'applicazione JavaScript di caricare a tempo di esecuzione codice proveniente da un'altra applicazione. Il codice caricato gira nel contesto dell'applicazione ospite e condivide le dipendenze, così da non duplicare librerie come React o Vue.
Il meccanismo consiste nell'esporre determinati moduli da un'applicazione remota e importarli in quella ospite. La remota dichiara quali moduli sono disponibili e l'ospite vi fa riferimento come se fossero import locali. Webpack si occupa del caricamento asincrono, della deduplicazione delle dipendenze e della risoluzione delle versioni in fase di costruzione.
La condivisione delle dipendenze è la caratteristica più importante. Quando ospite e remota usano la stessa libreria, Webpack garantisce che ne venga caricata una sola copia nel browser. Si evita così il peggioramento che deriverebbe da più copie di React, Lodash o altre librerie voluminose. Il modello a istanza unica assicura inoltre che lo stato condiviso — un archivio Redux o un contesto React — sia davvero condiviso fra i micro frontend.
// webpack.config.js - Remote application exposing a module
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "checkout",
filename: "remoteEntry.js",
exposes: {
"./CheckoutApp": "./src/bootstrap",
},
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true, requiredVersion: "^18.0.0" },
},
}),
],
};
// webpack.config.js - Host application consuming the remote
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "shell",
remotes: {
checkout: "checkout@http://localhost:3001/remoteEntry.js",
},
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true, requiredVersion: "^18.0.0" },
},
}),
],
};
// Lazy loading the remote module in the host
const CheckoutApp = React.lazy(() => import("checkout/CheckoutApp"));
function Shell() {
return (
<Suspense fallback={<Spinner />}>
<CheckoutApp />
</Suspense>
);
}L'ospite carica il file di ingresso remoto appena l'utente raggiunge una rotta che richiede il micro frontend di pagamento. Quel file è un piccolo JavaScript generato in automatico da Webpack: contiene l'elenco di tutti i moduli esposti e la loro posizione. Quando l'ospite chiede un modulo preciso, Webpack scarica il frammento corrispondente e lo esegue nel contesto delle dipendenze condivise.
Un aspetto importante è che ospite e remota devono concordare sulle versioni delle dipendenze condivise. Se la remota richiede React 18.2 ma l'ospite ha solo React 18.0, la condivisione a istanza unica fallisce a meno che le versioni non siano compatibili. Il campo requiredVersion accetta intervalli di versione semantica, lasciando margine e proteggendo al contempo dalle rotture.
Integrazione tramite iframe
Prima di Module Federation e delle tecniche moderne, l'iframe era l'approccio originario. Un iframe incorpora in una pagina un documento HTML del tutto indipendente. Quel documento ha il proprio contesto JavaScript, il proprio ambito CSS e il proprio albero DOM. Questo isolamento è insieme la forza e la debolezza del metodo.
L'iframe offre le garanzie di isolamento più solide fra tutte le tecniche. Nessun rischio di conflitti CSS, perché ogni iframe ha il proprio documento. Nessuna collisione JavaScript, perché ognuno ha il proprio ambito globale. Nessun micro frontend può alterare per sbaglio lo stato di un altro, essendo gli alberi DOM completamente separati. Una perdita di memoria in uno non può far cadere l'altro.
Queste garanzie costano care. Gli iframe sono pesanti. Ciascuno carica un documento HTML completo, con tutte le sue risorse CSS e JavaScript. Il browser tratta ognuno come un contesto di navigazione separato, con più memoria, più richieste di rete e più lavoro di disegno. Se incorporate dieci micro frontend in altrettanti iframe, il browser carica di fatto undici pagine.
La comunicazione fra un iframe e la pagina che lo contiene passa da postMessage, asincrono e limitato a dati serializzabili. Non è possibile passare funzioni, istanze di classe o riferimenti al DOM attraverso il confine. Gli iframe risultano quindi inadatti ai micro frontend che richiedono un'integrazione stretta — per esempio un modulo di pagamento che deve conoscere lo stato del carrello nella pagina che lo ospita.
Anche l'accessibilità ne risente. Lettori di schermo e altre tecnologie assistive faticano spesso con contesti di navigazione annidati. La navigazione da tastiera attraverso i confini degli iframe è incoerente. La ricerca nella pagina non li attraversa, e la cronologia tratta ogni navigazione interna come una sessione a sé.
Gli iframe si prestano soprattutto a incorporare contenuti di terzi, dove l'isolamento conta più di tutto e l'integrazione è minima. Sono una cattiva scelta per comporre un'esperienza coerente in cui i micro frontend devono condividere stato, coordinare la navigazione e apparire come un'unica superficie visiva.
Single-spa e altri quadri di orchestrazione
Single-spa è un quadro JavaScript che orchestra più micro frontend in un'unica pagina. Ne gestisce il ciclo di vita: li monta quando l'utente raggiunge una rotta che li richiede, li smonta quando se ne allontana e tiene in memoria quelli inattivi per rimontarli in fretta. È indipendente dal quadro applicativo e supporta React, Angular, Vue, Svelte e JavaScript puro.
Ogni micro frontend si registra presso single-spa come applicazione dotata di funzioni di ciclo di vita: bootstrap, mount, unmount e, facoltativamente, update. La radice di single-spa governa l'instradamento e decide, in base a schemi di URL, quali applicazioni sono attive. Quando l'URL corrisponde alla funzione di attività di un'applicazione registrata, single-spa la monta e smonta quelle non più attive.
Single-spa risolve uno dei problemi più ostici: la convivenza fra quadri applicativi. Quando due micro frontend sono costruiti con versioni diverse dello stesso quadro, o con quadri del tutto differenti, single-spa fa sì che possano coesistere nella stessa pagina senza conflitti. Ci riesce intervenendo sui ganci del ciclo di vita e isolando lo stato globale di ciascuna applicazione.
Altri approcci comprendono Piral, basato su un modello a estensioni in cui i micro frontend sono caricati come moduli da un servizio di distribuzione, e Open Components, incentrato sulla composizione lato server. C'è poi la via dei componenti web, in cui ogni micro frontend è avvolto in un elemento personalizzato e registrato presso l'interfaccia degli elementi personalizzati del browser. I componenti web offrono delimitazione nativa per HTML, CSS e JavaScript e si compongono in modo dichiarativo nei modelli HTML.
La scelta dipende dal vostro corredo tecnologico, dalla struttura delle squadre e dai requisiti di prestazione. Module Federation è la scelta migliore per chi già usa Webpack. Single-spa ha senso in architetture miste con quadri diversi. I componenti web si adattano alle organizzazioni che vogliono imporre confini indipendenti dal quadro. Gli iframe sono appropriati solo per incorporare contenuti di terzi.
Librerie di componenti e sistemi di progettazione condivisi
Una preoccupazione ricorrente è l'incoerenza visiva. Se ogni squadra costruisce la propria interfaccia per conto suo, l'utente può incontrare pulsanti, caratteri e impaginazioni diversi mentre si muove nell'applicazione. La risposta è una libreria di componenti o un sistema di progettazione condiviso, usato da tutti i micro frontend.
La libreria condivisa raccoglie di norma i componenti di presentazione: pulsanti, campi, finestre modali ed elementi di navigazione. Sono puramente visivi e non contengono logica di dominio. Accettano proprietà per varianti di stile, etichette e gestori di eventi, ma non recuperano dati, non gestiscono stato e non realizzano comportamenti specifici del dominio. Questa separazione mantiene stabile la libreria e le impedisce di diventare un collo di bottiglia per la velocità delle squadre.
La gestione delle versioni richiede metodo. Se la libreria è pubblicata come pacchetto npm, ogni micro frontend fissa una versione e aggiorna secondo i propri tempi. Ciò dà autonomia, ma può produrre uno scostamento di versioni, per cui lo stesso componente appare diverso in micro frontend diversi. Una batteria di prove di regressione visiva sulla libreria aiuta a cogliere queste discrepanze prima della produzione.
Un'alternativa è servire il sistema di progettazione come dipendenza a tempo di esecuzione. La libreria viene distribuita come applicazione autonoma e caricata da ogni micro frontend all'avvio. Tutti usano così sempre l'ultima versione di ogni componente e lo scostamento sparisce. In cambio, aggiornare il sistema richiede un rilascio e ogni rottura colpisce simultaneamente tutti i micro frontend.
I gettoni di progettazione come minimo comune denominatore
I gettoni di progettazione — valori con nome per colori, spaziature, caratteri e ombre — sono un'alternativa più leggera di una libreria completa. Ogni micro frontend realizza i propri componenti ma fa riferimento allo stesso insieme di gettoni. Si conserva così libertà realizzativa mantenendo coerenza visiva. I gettoni si distribuiscono come proprietà personalizzate CSS, facili da sovrascrivere e senza bisogno di alcuno strumento di costruzione.
Comunicazione fra micro frontend
I micro frontend devono parlarsi. Quello del pagamento deve sapere che cosa l'utente ha messo nel carrello dal catalogo. Quello dell'intestazione deve aggiornare l'indicatore quando quello delle notifiche ne riceve una nuova. Quello dell'accesso deve avvisare tutti gli altri quando l'utente entra o esce.
Il meccanismo più semplice è un bus di eventi condiviso. Ogni micro frontend pubblica eventi su un canale globale e si iscrive a quelli degli altri. Il bus si realizza di solito come emettitore di eventi agganciato all'oggetto window o iniettato dal guscio. Gli eventi portano un tipo e un carico; chi si iscrive filtra per tipo.
// Shared event bus - shell application
// Each micro frontend receives this bus as part of its initialization.
type BusEvent = {
type: string;
payload: unknown;
};
type Listener = (event: BusEvent) => void;
class EventBus {
private listeners: Map<string, Listener[]> = new Map();
on(type: string, listener: Listener): () => void {
if (!this.listeners.has(type)) {
this.listeners.set(type, []);
}
this.listeners.get(type)!.push(listener);
return () => this.off(type, listener);
}
off(type: string, listener: Listener): void {
const listeners = this.listeners.get(type);
if (listeners) {
this.listeners.set(
type,
listeners.filter((l) => l !== listener)
);
}
}
emit(type: string, payload: unknown): void {
this.listeners.get(type)?.forEach((listener) => {
listener({ type, payload });
});
}
}
// Usage in a micro frontend
export function init(bus: EventBus) {
bus.on("item:added-to-cart", (event) => {
updateCartBadge(event.payload.quantity);
});
bus.emit("user:logged-in", { userId: "abc-123", name: "Jane" });
}Per esigenze più complesse, un archivio di stato condiviso è spesso preferibile a un bus di eventi. Un archivio Redux o Zustand ospitato nel guscio può essere iniettato in ogni micro frontend. Ciascuno vi legge lo stato che attraversa i domini e scrive nel proprio archivio locale lo stato specifico del suo dominio. L'accoppiamento resta lasco e si ottiene comunque un'unica fonte di verità per i dati condivisi: l'utente corrente, il contenuto del carrello, la navigazione attiva.
Lo strato di comunicazione va progettato e documentato in modo esplicito. Le squadre devono accordarsi su nomi degli eventi, forma dei carichi e confine fra stato condiviso e stato locale. Senza questo accordo, i micro frontend sviluppano dipendenze implicite dallo stato interno altrui, dando vita a un monolite distribuito che ha tutta la complessità dei micro frontend e nessuno dei loro vantaggi.
Strategie di instradamento
L'instradamento deve rispondere a due domande: a quale micro frontend appartiene la rotta corrente? e come funziona la navigazione fra loro? Le risposte dipendono dal fatto che usiate un instradatore centralizzato nel guscio, un approccio distribuito o una strategia mista.
Nell'instradamento centralizzato è un'applicazione guscio a possedere l'instradatore di primo livello. Definisce la mappa delle rotte, stabilisce quale micro frontend montare per ogni schema di URL e gli passa i parametri. Ogni micro frontend ha il proprio instradatore interno per muoversi dentro il suo dominio. Il guscio governa i passaggi fra domini; i micro frontend, i percorsi interni.
Nell'instradamento distribuito le rotte appartengono a ciascun micro frontend. Il guscio continua a gestire la corrispondenza generale fra URL e micro frontend, ma ognuno amministra da sé le proprie sottorotte e la navigazione interna. Una libreria di navigazione nel guscio coordina i cambi di cronologia e assicura che la barra degli indirizzi rifletta lo stato dell'intera applicazione, non solo del micro frontend attivo.
Nell'instradamento a eventi è il bus a coordinare la navigazione. Quando un micro frontend deve raggiungere una rotta che appartiene a un altro, emette un evento di navigazione. Il guscio lo ascolta e compie lo spostamento, montando la destinazione. Le questioni di instradamento restano così fuori dai micro frontend e la logica si concentra nel guscio.
- Instradamento centralizzato: il guscio possiede la mappa delle rotte, i micro frontend gestiscono solo la navigazione interna. Ideale per squadre piccole e confini semplici.
- Instradamento distribuito: ogni micro frontend amministra le proprie sottorotte. Ideale per squadre grandi con domini nettamente separati.
- Instradamento a eventi: la navigazione è coordinata da un bus condiviso. Ideale per architetture miste con quadri applicativi diversi.
- Instradamento ibrido: il guscio governa le rotte di dominio di primo livello, i micro frontend le sottorotte. Ideale per la maggior parte delle applicazioni in produzione.
Rilascio e gestione delle versioni
L'indipendenza nel rilascio è la ragione principale per adottare i micro frontend. Ogni squadra deve poter rilasciare il proprio senza coordinarsi con le altre e senza compromettere la stabilità dell'insieme. Per riuscirci occorre progettare con cura la catena di rilascio, la strategia di hosting e lo schema delle versioni.
Il modello più semplice è l'hosting separato. Ogni micro frontend è distribuito al proprio indirizzo o nel proprio spazio di archiviazione. Il guscio lo carica da lì a tempo di esecuzione. Questo modello offre la massima indipendenza, perché ogni squadra controlla la propria infrastruttura, la propria catena e la propria strategia di ritorno indietro. Il guscio non va ridistribuito quando un micro frontend si aggiorna, perché ne carica dinamicamente l'ultima versione.
L'hosting separato introduce una difficoltà nuova: coordinare i rilasci per preservare la compatibilità all'indietro. Se il micro frontend di pagamento si aspetta una certa forma di proprietà dal guscio e il rilascio successivo del guscio la cambia, il pagamento si rompe finché non viene aggiornato a sua volta. La soluzione è versionare il contratto d'integrazione — di norma le proprietà passate dal guscio — e segnalare le rotture con un versionamento semantico.
I rilasci graduali e le prove su una piccola quota di utenti sono più facili qui, perché ogni micro frontend si rilascia a sé. Una squadra può distribuire una versione nuova al cinque per cento degli utenti, sorvegliare i tassi di errore e gli indicatori di prestazione, e poi ampliare a poco a poco se tutto va bene. In caso di problemi torna indietro solo sul proprio micro frontend, senza toccare il resto.
Fissare una versione dal guscio è l'uscita di sicurezza. Se un rilascio introduce una rottura sfuggita alle prove, il guscio può fissare la versione precedente e ristabilire la stabilità mentre la squadra risolve. Il meccanismo è di solito un file di configurazione o una variabile d'ambiente che associa a ogni micro frontend una versione già distribuita.
Repository unico o repository separati
La struttura dei repository è argomento discusso nella comunità del frontend. Le ragioni a favore del repository unico e quelle a favore dei repository separati hanno entrambe valore, e la scelta giusta dipende dalla dimensione delle squadre, dalla maturità dell'organizzazione e dalle preferenze di strumenti.
Un repository unico tiene tutti i micro frontend in un solo repository, ordinati per cartelle. Ognuno ha il proprio package.json, la propria configurazione di costruzione e la propria cartella. Questo approccio si serve di strumenti come Turborepo, Nx, Lerna o gli spazi di lavoro di pnpm per gestire le dipendenze, eseguire le costruzioni e orchestrare le attività fra micro frontend.
- La configurazione condivisa degli strumenti è semplice: un solo ESLint, un solo TypeScript, un solo Prettier per tutti.
- Le riorganizzazioni trasversali sono più semplici, perché il codice di tutti sta in un unico posto.
- La gestione delle dipendenze è centralizzata, riducendo il rischio di versioni disallineate.
- Sono possibili commit atomici fra micro frontend quando una modifica tocca più domini.
L'approccio a più repository colloca ogni micro frontend nel proprio, con la sua catena e la sua configurazione. Le squadre mantengono piena autonomia sul corredo tecnologico e sul processo di pubblicazione. Una squadra che voglia passare da React a Preact può farlo senza coordinarsi, purché il suo micro frontend continui a comparire correttamente nel guscio.
- Le squadre hanno autonomia completa sul proprio flusso di lavoro e sugli strumenti.
- La dimensione del repository resta contenuta, con cloni rapidi e catene efficienti.
- Non servono strumenti particolari per il repository unico: bastano i flussi Git consueti e i registri npm.
- Una rottura in un micro frontend non può far fallire la costruzione di un altro.
La tendenza del settore propende per il repository unico, soprattutto dove si condivide un sistema di progettazione o un insieme di utilità. Il costo operativo di coordinare le modifiche fra più repository supera spesso il guadagno di autonomia, in particolare per squadre già vicine e con corredi simili. Per organizzazioni con squadre disperse e quadri applicativi diversi, i repository separati restano però la scelta migliore.
Considerazioni sulle prestazioni
I micro frontend comportano un costo prestazionale rispetto a un'applicazione monolitica. Ognuno carica il proprio pacchetto JavaScript, il proprio CSS ed eventualmente il proprio ambiente di esecuzione del quadro. Il browser deve scaricare, analizzare ed eseguire più codice. I byte trasferiti aumentano e il tempo prima che la pagina sia utilizzabile al primo caricamento si allunga.
La condivisione delle dipendenze di Module Federation attenua il costo garantendo che una libreria venga caricata una sola volta. Ma il codice applicativo — componenti, utilità e logica di dominio di ciascun micro frontend — non è condiviso. Se l'utente attraversa tre micro frontend in una sessione, scarica il codice di tutti e tre, anche se ne usa uno solo.
Suddividere il codice dentro ogni micro frontend è indispensabile. Ciascuno dovrebbe caricare in modo differito le proprie rotte, i componenti pesanti e le librerie di terzi. Un micro frontend di amministrazione con una libreria di grafici non dovrebbe caricarla finché l'utente non raggiunge una pagina che ne disegna uno. È la stessa pratica delle applicazioni monolitiche, applicata dentro ogni confine.
Il precaricamento è un'ottimizzazione preziosa. Quando l'utente entra in una rotta del micro frontend di pagamento, il guscio può prevedere che proseguirà verso la conferma d'ordine e iniziare a recuperarne le risorse in secondo piano. La strategia va fondata su dati reali di utilizzo, non su supposizioni.
Il percorso critico di disegno va protetto. Il guscio deve comparire con la stessa rapidità di un'applicazione monolitica, quindi restare leggero: poco JavaScript, poco CSS, poche dipendenze. Risponde del primo disegno, e micro frontend lenti non devono ritardarlo. Ciascuno va caricato in modo asincrono e deve mostrare uno stato di attesa finché non è pronto.
I budget di prestazione vanno fissati per singolo micro frontend, non solo per l'applicazione intera. Ogni squadra deve conoscere la dimensione massima del proprio pacchetto, il tempo massimo prima dell'usabilità e l'ingombro massimo di memoria. Il loro superamento dovrebbe far fallire la catena, come accadrebbe in un'applicazione monolitica.
Provare i micro frontend
Provare questa architettura richiede tre livelli: ciascun micro frontend isolato, l'integrazione fra loro e l'applicazione composta nel suo insieme. Ogni livello ha strumenti, obiettivi e responsabilità propri.
Le prove unitarie e di componente dentro ogni micro frontend seguono gli schemi di qualsiasi applicazione frontale. Ogni squadra prova il proprio e le prove girano nella sua catena. L'unica differenza è che conviene provare presumendo che il guscio e gli altri micro frontend siano inaffidabili: verificare con prudenza che il proprio degradi con garbo quando il guscio fornisce proprietà inattese o un evento non arriva.
L'integrazione è il livello più arduo. La prova deve caricare il guscio e più micro frontend insieme, riprodurre interazioni che attraversano i confini e verificare che il comportamento composto sia corretto. Cypress e Playwright eccellono qui, perché girano in un browser vero e manovrano l'applicazione completa.
Un metodo efficace è tenere, in un ambiente di prova, un rilascio simile alla produzione: ogni micro frontend al proprio indirizzo e il guscio configurato per caricarli. La batteria gira contro quel rilascio e riproduce percorsi reali che attraversano i confini. Emergono così problemi invisibili in isolamento: difetti di sincronia quando un micro frontend non ha finito di inizializzarsi mentre un altro gli parla già, o regressioni di stile quando una modifica CSS in uno sposta l'impaginazione di un altro.
Le prove di regressione visiva qui contano in modo particolare. Il sistema di progettazione condiviso dovrebbe avere la propria batteria. Ogni micro frontend dovrebbe averne una per le proprie pagine. E l'applicazione composta dovrebbe averne per i percorsi critici. Percy, Chromatic o Loki si inseriscono nella catena e colgono automaticamente gli scostamenti.
Quando NON usare i micro frontend
I micro frontend non sono l'architettura giusta per ogni progetto. La complessità che introducono — costo operativo, costo prestazionale, bisogno di coordinamento e difficoltà di indagine — si giustifica solo con esigenze organizzative precise. Applicarli a un progetto che non ne ha bisogno crea attrito e rallenta lo sviluppo senza contropartita.
Una squadra piccola che costruisce una sola applicazione non ne ha bisogno. L'architettura è pensata per organizzazioni con più squadre, ciascuna responsabile di un dominio distinto. Se l'intero frontend può essere costruito da tre o quattro persone, un'applicazione monolitica con moduli ben ordinati sarà più produttiva, più rapida da costruire e più facile da mantenere.
Un'applicazione con domini strettamente accoppiati è una cattiva candidata. Se ogni funzionalità interagisce con tutte le altre — se catalogo, carrello e pagamento sono tanto intrecciati da non potersi separare con pulizia — dividerli vi costringerà a costruire strati di comunicazione complessi che imitano l'accesso diretto del monolite. È l'antischema del monolite distribuito.
Un'organizzazione priva di maturità operativa farà fatica. L'architettura richiede buone pratiche di esercizio, prove automatizzate, sorveglianza e risposta agli incidenti. Ogni squadra deve poter rilasciare da sé e tornare indietro in fretta. Se queste basi mancano, conviene costruirle prima attorno a un'applicazione monolitica.
Le applicazioni critiche nelle prestazioni, con requisiti stringenti sui tempi di caricamento, potrebbero non tollerare questo costo. Se ogni millisecondo conta — per esempio in una postazione di negoziazione in tempo reale o in un'interfaccia video — le richieste di rete in più e il tempo aggiuntivo di analisi del JavaScript possono far sforare il budget.
- La vostra squadra ha meno di quattro sviluppatori frontend.
- L'applicazione ha meno di cinque confini di dominio ben distinti.
- La squadra non ha esperienza di microservizi o sistemi distribuiti.
- All'organizzazione mancano integrazione continua e sorveglianza automatizzate.
- L'applicazione ha budget di prestazione stringenti, già difficili da rispettare.
- Le funzionalità sono strettamente accoppiate e non si separano con pulizia in domini.
I micro frontend sono una decisione organizzativa che si manifesta come architettura tecnica. Se non vi serve l'indipendenza organizzativa che offrono — squadre diverse, ritmi diversi, scelte tecniche diverse — il costo tecnico non vale la pena.
Conclusione
I micro frontend sono uno schema potente per organizzazioni che devono scalare lo sviluppo frontale su più squadre. L'architettura porta vantaggi reali: rilascio indipendente, autonomia delle squadre, libertà tecnologica e possibilità di pubblicare a ritmi diversi. Sono vantaggi concreti e misurabili quando l'architettura è applicata al problema giusto.
La chiave del successo sta nel trattarli prima come soluzione organizzativa e poi come soluzione tecnica. L'architettura esiste per rendere indipendenti le squadre, non per produrre codice elegantemente disaccoppiato. Ogni decisione tecnica — Module Federation o iframe, repository unico o più repository, bus di eventi o archivio condiviso — va presa mirando ad aumentare la velocità delle squadre senza perdere un'esperienza coerente.
Cominciate in piccolo. Partite da un solo micro frontend con un confine di dominio netto e una squadra motivata a portarlo avanti da sé. Dimostrate che la catena di rilascio funziona, che le prestazioni reggono e che la squadra consegna più in fretta. Poi allargate per gradi. Non è tutto o niente: un approccio misto — uno o due micro frontend dentro un'applicazione per il resto monolitica — è spesso il miglior punto di partenza.
Il fallimento più comune è l'astrazione prematura. Le squadre costruiscono elaborati strati di orchestrazione, sistemi di stato condiviso e infrastrutture trasversali prima di conoscere i bisogni reali. Partite dall'integrazione più semplice possibile — un guscio che carica i micro frontend con un tag di script o con Module Federation — e aggiungete complessità solo quando il semplice si dimostri insufficiente. Il giusto grado di astrazione si rivela con l'uso, non sulla carta.
Module Federation ha reso i micro frontend più accessibili che mai, ma le domande di fondo restano organizzative. Le vostre squadre hanno bisogno di rilasciare in modo indipendente? La vostra organizzazione regge la complessità operativa? I vostri domini si separano con pulizia? Chi risponde di sì li troverà trasformativi. Chi risponde di no li troverà frustranti. L'architettura in sé è neutra: il suo valore dipende interamente dal contesto in cui la si applica.