Module Federation a React Native

Per què Module Federation a React Native

La versió curta: Module Federation permet que una app de React Native carregui les seves features en temps d’execució, de manera que cadascuna es pot desplegar i actualitzar pel seu compte en lloc de viatjar en una sola publicació a l’App Store. Això et dona desplegaments independents i correccions over-the-air. També et posa a sobre un problema de sistemes distribuïts que abans era feina del bundler. Aquesta sèrie construeix un setup federat funcional des de zero sobre una petita app Pokédex. Aquest primer post tracta de si ho hauries de fer.

Cada feature viatja en cada publicació

Una app de React Native estàndard és un sol bundle. Organitza-la bé, per feature en lloc de per tipus, i això no canvia res aquí: la pantalla de login, la pàgina de configuració, l’informe que ningú obre, tot compilat junt, tot passant per la mateixa revisió de l’store, tot en el mateix tren de releases. Una correcció d’una línia en una pantalla espera que tota l’app es reconstrueixi, es torni a enviar i s’aprovi.

Per a una app petita amb un sol equip, no passa res. El tren de releases és barat i tothom hi va de totes maneres. Per a una app gran amb diversos equips, és car. La correcció urgent d’un equip queda darrere de la feature a mig fer d’un altre perquè comparteixen un binari. La publicació es converteix en una negociació i la cadència cau al ritme de l’equip que triga més a pujar-hi.

Aquest acoblament és el que Module Federation intenta desfer. No la mida del bundle, no la velocitat de build, això són efectes secundaris agradables. El premi de veritat és trencar el lligam entre «he canviat la meva feature» i «tota l’app s’ha de publicar».

Què és en realitat

Una app federada té un host i un conjunt de remotes. El host és el shell: navegació, la tab bar, les biblioteques compartides, les peces que sempre hi són. Els remotes són les features, i cadascun es construeix i es desplega pel seu compte, després s’incorpora en temps d’execució des d’una URL.

El host no compila els remotes dins seu com fa un sol bundle. Module Federation no et resol per si sol res de la part offline: encastar una còpia de cada remote al binari, perquè l’app revisada funcioni sola i sense xarxa, és una arquitectura que et toca muntar a tu, i que aquesta sèrie munta més endavant. La versió en viu ve d’un CDN (una xarxa de distribució de continguts) i s’actualitza sense una publicació. El host també aporta un sol cop les biblioteques pesades que es comparteixen, React, el navigation stack, la capa d’estils, així cada remote consumeix la còpia del host en lloc de portar la seva. Un remote esdevé un payload petit de codi de feature que encaixa en un shell que ja sosté tot el que hi ha a sota.

A la pràctica això s’executa sobre Re.Pack (Rspack per sota) amb Module Federation 2.0. La mecànica la veiem en un post posterior. De moment amb el model mental ja n’hi ha prou: un shell que carrega features en temps d’execució, des de la xarxa o d’un fallback incrustat, contra un contracte sobre què proporciona el shell.

Què t’aporta

Desplegaments independents. Un equip de feature publica quan la seva feature està llesta, no quan surt el tren. La publicació deixa de ser un recurs compartit pel qual tothom fa cua.

Correccions over-the-air. Un bug en un remote és tornar a pujar aquell remote, no un enviament a l’store. La correcció està publicada en minuts, i cada usuari l’agafa a la següent arrencada, dins de les regles de la plataforma (Què costa les sospesa).

Arrencades més ràpides. Les features que no calen a l’arrencada es carreguen de manera lazy, així s’executa menys JavaScript al camí crític. La descàrrega en si no es redueix si envies un fallback offline, el binari segueix portant cada remote, però l’arrencada sí.

Autonomia d’equip a escala. Cada feature té el seu propi build, el seu propi desplegament, la seva pròpia cadència. L’arquitectura deixa de forçar els equips a anar tots a una.

Si res d’això no et fa mal de veritat, pots deixar de llegir aquí. La federació resol l’acoblament. Sense acoblament, cap motiu per pagar la solució.

Què costa

Aquesta és la part que els posts entusiastes es salten, així que és la part que val la pena anar a poc a poc.

El contracte de singletons compartits. El host proporciona un React, una biblioteca de navegació, una capa d’estils, i cada remote es renderitza contra aquestes còpies. En el moment que un remote necessita una versió més nova d’una biblioteca compartida que la que porta el host, tens un problema de version skew. Si no ho gestiones, el resultat depèn de com estigui configurada l’entrada compartida i no sempre acaba en la còpia del host: la resolució de singletons tria una sola versió per a tothom i pot quedar-se amb la més alta compatible, així que qualsevol dels dos costats pot acabar executant-se contra una còpia per a la qual no es va construir. Té solució, la sèrie construeix la correcció, però resoldre-ho és el cost: el conjunt compartit esdevé un contracte del qual respons tu i que has de mantenir compatible, una feina que abans feia el compilador de franc.

La càrrega de compatibilitat, sobretot per a versions antigues de l’app. Els usuaris no actualitzen tots. Un binari que algú es va instal·lar fa mesos té les biblioteques compartides congelades a allò que es va publicar llavors. Publica un remote que necessiti unes de més noves i trenques justament qui no s’ha actualitzat. Així que acabes mantenint disponibles versions antigues del remote per a versions antigues de l’app, la mateixa disciplina que mantenir viu un endpoint d’API antic fins que l’últim client deixa de cridar-lo. Això no és feina de bundler. Això és fer funcionar un servei versionat.

Integritat. Un cop la teva app descarrega i executa codi des d’una URL, aquella URL és una superfície d’atac. Has de signar el que envies i fer que el dispositiu ho verifiqui abans d’executar-ho, o un host compromès pot donar als teus usuaris el que vulgui. Després has de protegir també la tria de versió, perquè un manifest repetit o revertit no pugui servir en silenci un build antic i vulnerable. La seguretat que et donava de franc un sol binari signat ara te l’has de construir tu.

Module Federation és un problema de sistemes distribuïts vestit de bundler.

Regles de plataforma. Aquí manen dos documents d’Apple, i cadascun traça una línia diferent. El Developer Program License Agreement (secció 3.3.1(B)) permet que una app executi codi interpretat descarregat, com ara JavaScript, amb tres condicions: aquest codi no pot canviar el propòsit principal de l’app, no pot muntar cap botiga ni cap aparador per a altre codi o altres apps, i no pot saltar-se la signatura, el sandbox ni cap altra mesura de seguretat del sistema operatiu. La directriu 2.5.2 de revisió encara estreny més: el binari que envies ha de funcionar per si sol, i no pot descarregar, instal·lar ni executar codi que introdueixi o canviï funcionalitats de l’app. Els dos documents s’apliquen alhora, i complir les condicions de la llicència no demostra que passis la revisió: la redacció de 2.5.2 arriba a qualsevol codi descarregat que canviï què fa l’app, no només al que queda fora del propòsit revisat. La lectura amb què operen els serveis OTA és mantenir el que s’envia per aire dins de la funcionalitat que Apple ja ha revisat, en forma de correccions i ajustos, acceptant que la redacció de la directriu deixa a Apple marge per discrepar.

Superfície operativa. Un CDN per operar, caches per invalidar, rollbacks per programar, fallades per monitoritzar. Quan un remote no es pot carregar, l’app s’ha de degradar a alguna cosa segura en lloc de mostrar una pantalla en blanc. Aquesta xarxa de seguretat és enginyeria de veritat, i recau en tu.

Tot junt, la part de bundler té un final clar: la configures i ja està. La part de sistemes (signatura, versionat, compatibilitat, fallback) és la feina de veritat, i no s’acaba mai del tot.

Quan val la pena

Recorre-hi quan totes tres siguin certes:

  • Diversos equips s’estan trepitjant en una publicació compartida.
  • L’acoblament és un cost mesurat, cadència més lenta, correccions bloquejades, no un de teòric.
  • Algú pot fer-se càrrec de la plataforma, el CDN, la signatura, el contracte de versions, la capa de fallback, com a feina contínua.

Salta-t’ho quan l’app és petita, la manté un sol equip, i una publicació a l’store cada parell de setmanes no és cap càrrega. La complexitat que assumiries empetiteix l’acoblament que trauries. El code splitting sol, chunks asíncrons sense la maquinària de runtime-remote, et dona el lazy-loading i l’arrencada més ràpida a una fracció del cost, i és un punt sensat per aturar-se abans de la federació completa.

La federació és una eina per escalar. Adopta-la perquè has arribat a l’escala que la justifica, no perquè l’arquitectura sigui interessant. Ho és. Aquesta és la trampa.

Què fa la resta d’aquesta sèrie

A partir d’aquí és pràctic. Construïm un setup federat sobre una petita app Pokédex i el portem fins al final, en un arc de disset parts:

  • un host i un primer remote que es carrega en temps d’execució, després el contracte de singletons compartits i la trampa que el fa fallar en silenci
  • la frontera mateixa: un paquet de contracte tipat, un store compartit i qui respon de què quan diversos equips comparteixen runtime
  • estat de client, dos stacks d’estat comparats sobre la mateixa app, dos backends en un sol cache i un design system compartit com a singleton
  • accessibilitat a través de la frontera, i el relleu entre React Native i el shell natiu
  • el build de producció, el lliurament per CDN amb un mapa de versions, un fallback offline dins del binari i un mapa signat des del qual l’app es pot revertir sola

Al final tindràs una versió funcional de tot el que aquest post acaba d’advertir-te, i una idea clara de si a la teva app li compensa.

Fonts

Warren de Leon
Warren de Leon

Software Engineering Manager. Recentment he liderat l'equip de Mobile Platform a Hargreaves Lansdown. Escric sobre lideratge tècnic, React Native i com construir bons equips.

Veure perfil