# Micro-frontend - conception JAMStack

# Vue d'ensemble

L'objectif de la conception micro-frontend - JAMStack est de créer un framework qui :

  • facilite la collaboration de la communautĂ© (en permettant le dĂ©veloppement indĂ©pendant de composants)
  • rend les extensions ou personnalisations faciles
  • permet aux membres de la communautĂ© de contribuer en retour Ă  l'OSS sans forker l'ensemble du code source

# Micro-frontends

Le framework utilise des micro-frontends comme moyen de découpler les parties de l'UI pour permettre des bases de code maintenables, des équipes autonomes, des publications indépendantes et des mises à niveau progressives de parties de l'UI.

# JAMStack

L'implémentation JAMStack (opens new window) réduit le rÎle du serveur web à la distribution de fichiers de balisage statiques, en maintenant la fonctionnalité dans JavaScript (qui s'exécute dans le navigateur client) et dans l'API backend.

JAMStack signifie :

J - JavaScript A - APIs M - Static markup (balisage statique)

Cette implémentation de stack est considérée comme une bonne pratique car elle :

  • est beaucoup plus simple Ă  sĂ©curiser
  • offre une bonne expĂ©rience client grĂące Ă  des temps de rĂ©ponse web rapides
  • est peu coĂ»teuse Ă  hĂ©berger

De plus, les éléments suivants ont également été conçus et font normalement partie d'une implémentation JAMStack :

  • dĂ©ploiement sur un rĂ©seau de distribution de contenu (CDN)
  • dĂ©ploiements atomiques
  • utilise des micro-frontends chargĂ©s dynamiquement, de sorte que la mise Ă  jour vers la derniĂšre version est automatique

# Pile technologique

  1. React Le framework est basĂ© sur la bibliothĂšque React. C'est la bibliothĂšque Single Page Application (SPA) la plus populaire en usage, et de plus ce choix nous permet de capitaliser sur d'autres efforts communautaires facilitant une conversion facile vers cette bibliothĂšque. Elle peut ĂȘtre amĂ©liorĂ©e en utilisant des bibliothĂšques de conteneurs d'Ă©tat (Redux, Flux, MobX), mais il n'y a aucune restriction sur une utilisation spĂ©cifique. Les micro-frontends sont livrĂ©s avec des stores Redux prĂ©configurĂ©s et isolĂ©s.

  2. Webpack 5 Webpack 5 est actuellement le seul bundler JavaScript qui supporte la séparation de build à distance. Cela se fait en utilisant le Module Federation Plugin. Il permet une composition au moment de l'exécution pour offrir une expérience utilisateur fluide et entiÚrement transparente, résultant en une Single Page Application traditionnelle. Il y a des avantages supplémentaires par rapport aux autres technologies, résultant tous en une empreinte réduite et une meilleure expérience globale pour les utilisateurs. Webpack 5 implémentera l'intégration host/child micro-frontends au moment de l'exécution.

  3. CI/CD et déploiements atomiques (par exemple, Github Actions) Chaque implémentation du Business Operations Framework devra implémenter sa propre solution de déploiement atomique. Le projet Business Operations standard utilisera Github Actions pour exécuter le pipeline d'intégration continue, exécuter les tests pertinents, construire le micro-frontend individuel et déployer les fichiers statiques résultants sur un CDN et/ou créer une image Docker. Chaque micro-frontend est publié en complÚte autonomie : l'application composée peut utiliser les versions mises à jour de chaque micro-frontend individuel automatiquement, sans nécessiter de coordination supplémentaire.

  4. Fonctionnement sur un CDN Les micro-frontends peuvent fonctionner sur un CDN. Les builds individuels sont composĂ©s uniquement de fichiers statiques (HTML, CSS, JavaScript) et peuvent ĂȘtre dĂ©ployĂ©s dans diffĂ©rents emplacements / diffĂ©rentes URLs. Tant qu'ils sont disponibles via une connexion sĂ©curisĂ©e (HTTPS), les micro-frontends peuvent ĂȘtre servis depuis n'importe quel emplacement et Ă©galement depuis diffĂ©rents CDN.

  5. Fonctionnement dans Kubernetes Les micro-frontends peuvent fonctionner dans un environnement Kubernetes. Deux approches peuvent ĂȘtre adoptĂ©es ici :

    • Les micro-frontends individuels et l'application shell sont conteneurisĂ©s (par exemple avec Docker) puis hĂ©bergĂ©s dans Kubernetes. L'hĂŽte et les applications enfants peuvent ĂȘtre dĂ©ployĂ©s sur le mĂȘme cluster ou sur des clusters diffĂ©rents tant qu'ils sont accessibles publiquement.
    • DĂ©ployer un CDN privĂ© dans le cluster Kubernetes et hĂ©berger les fichiers de balisage statiques sur le CDN. Divers CDN compatibles avec Kubernetes sont disponibles.

# Construction Webpack

L'hĂŽte et les applications enfants incluent des scripts pour construire les artefacts de distribution. La construction peut ĂȘtre effectuĂ©e sur la machine hĂŽte du dĂ©veloppeur, dans le CI et dans Docker.

# Chargement des micro-frontends

L'hÎte est responsable du chargement des applications enfants au moment de l'exécution. Il recueille des informations sur les enfants disponibles au moment de l'exécution, soit depuis une API soit depuis un registre.

L'hĂŽte inclut un moteur interne responsable du chargement uniquement des enfants nĂ©cessaires lorsqu'ils doivent ĂȘtre affichĂ©s.

Les micro-frontends individuels ne seront pas chargés lorsque ce n'est pas nécessaire (par exemple, lorsqu'une page spécifique n'est pas accédée par l'utilisateur).

Diagramme de séquence de haut niveau illustrant comment les microservices sont chargés Diagramme de séquence de haut niveau illustrant comment les microservices sont chargés

# Référentiel de micro-frontends

Afin de fournir une autorité centralisée responsable du contrÎle des micro-frontends individuels répondant aux exigences nécessaires, il est suggéré de construire une solution qui fonctionne comme un registre.

Le registre servirait les objectifs suivants :

  1. permettre à la communauté d'enregistrer les micro-frontends et de spécifier certains détails
  2. exposer une API utilisée par l'hÎte pour récupérer des informations sur les micro-frontends disponibles
  3. fournir des informations sur les versions des micro-frontends disponibles

Le registre n'existe pas encore, et il n'est pas judicieux de le créer pour le moment.

# Déploiements

Le diagramme de vue d'ensemble suivant montre le déploiement des micro-frontends sur un CDN.

REMARQUE

Le déploiement de l'API de contexte délimité n'est pas couvert dans ce diagramme.

Diagramme de vue d'ensemble montrant le déploiement

Les micro-frontends utilisent des déploiements atomiques et aucun build complet n'est jamais requis.

Chaque micro-frontend individuel se déploie indépendamment des autres.

# Intégration Continue / Livraison Continue (CI/CD)

Chaque micro-frontend a sa propre configuration CI/CD ; il n'est pas nĂ©cessaire de partager la mĂȘme configuration ou d'utiliser le mĂȘme outil CI.

Le CI/CD peut ĂȘtre configurĂ© pour supporter plusieurs environnements, par exemple DEV, QA, PROD.

Voici un exemple de fichier montrant un flux de travail git action.

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x]


    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2

    # Runs a single command using the runners shell
    - name: Use NodeJS ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - name: Cache node modules
      uses: actions/cache@v2
      env:
        cache-name: cache-node-modules
      with:
        # npm cache files are stored in `~/.npm` on Linux/macOS
        path: '**/node_modules'
        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
        restore-keys: |
          ${{ runner.os }}-build-${{ env.cache-name }}-
          ${{ runner.os }}-build-
          ${{ runner.os }}-
    - run: yarn install --frozen-lockfile
    - run: yarn lint
    - run: yarn test
    - run: yarn build
#    - name: Slack Notification
#      uses: rtCamp/action-slack-notify@v2.0.2
#      env:
#        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

# CDN

L'application SPA résultante est servie par un CDN ou plusieurs CDN. Les micro-frontends individuels peuvent résider dans différents CDN.

# Kubernetes

L'application SPA rĂ©sultante peut fonctionner et ĂȘtre servie dans un ou plusieurs environnements Kubernetes.

# Application hĂŽte

L'application hĂŽte est livrĂ©e avec une configuration prĂ©configurĂ©e prĂȘte Ă  l'emploi. Elle ne nĂ©cessite aucune configuration particuliĂšre diffĂ©rente d'un SPA traditionnel, autre que la configuration du Module Federation de Webpack 5. Elle agira comme l'orchestrateur, chargeant les micro-frontends distants et leur fournissant des fonctionnalitĂ©s Ă  l'Ă©chelle de l'application, par exemple l'authentification, le RBAC, le routage cĂŽtĂ© client.

Il n'y a pratiquement aucune limite Ă  la façon dont l'hĂŽte peut croĂźtre et ĂȘtre Ă©tendu. Il est cependant suggĂ©rĂ© de centraliser toutes les communications hĂŽte-enfant et les composants partagĂ©s dans une bibliothĂšque externe afin que l'hĂŽte et les enfants aient la mĂȘme connaissance et que l'intĂ©gration ne se brise pas.

# Versionnement des micro-frontends

​L'approche suggĂ©rĂ©e est de construire un registre oĂč les applications individuelles sont enregistrĂ©es. Le registre permettrait de dĂ©finir une configuration sur chaque application et de suivre toutes les versions disponibles. ​ Le registre exposerait ensuite une API consommĂ©e par l'hĂŽte, fournissant des informations sur les micro-frontends disponibles, les versions et les emplacements des artefacts. ​ Le registre serait administrĂ© par un opĂ©rateur de confiance via une interface utilisateur ; il serait de la responsabilitĂ© de l'opĂ©rateur de confiance de dĂ©cider quelle version de chaque application individuelle serait rendue publique et disponible pour l'hĂŽte Ă  charger. Il permettrait Ă©galement de tester facilement des versions et de les annuler si nĂ©cessaire, tout cela sans avoir besoin de reconstruire et de redĂ©ployer les applications. ​

REMARQUE

Les artefacts de build JS créés par Webpack n'incluent pas la version dans le nom du fichier. Il pourrait ĂȘtre nĂ©cessaire de mettre Ă  jour le build afin de diffĂ©rencier les versions. Une approche plus simple qui ne nĂ©cessite pas de mettre Ă  jour la configuration de build serait d'hĂ©berger les versions sur diffĂ©rentes URLs.

​

# Mise Ă  niveau de l'hĂŽte

​L'hĂŽte est assez bien isolĂ© et la seule chose nĂ©cessaire pour faire un versionnement correct est d'utiliser la commande intĂ©grĂ©e yarn version. Elle crĂ©era un nouveau tag git et incrĂ©mentera la version de package.json selon la façon dont la commande est utilisĂ©e (CLI interactif). ​

# Mise Ă  niveau des remotes

​Les remotes sont isolĂ©s et la seule chose nĂ©cessaire pour faire un versionnement correct est d'utiliser la commande intĂ©grĂ©e yarn version. Elle crĂ©era un nouveau tag git et incrĂ©mentera la version de package.json selon la façon dont la commande est utilisĂ©e (CLI interactif).

# Composition Menu / Application

​L'hĂŽte est configurĂ© pour construire dynamiquement la structure Menu et Pages (avec react-router). Actuellement, le(s) composant(s) Menu est importĂ© de la bibliothĂšque @modusbox/react-components. ​ Il n'est pas strictement nĂ©cessaire d'utiliser de tels composants et l'hĂŽte / les remotes pourraient utiliser des composants personnalisĂ©s, Ă  condition qu'ils permettent la composition dynamique et supportent le routage. ​​

# Motivation des micro-frontends en détail

​La construction d'interfaces utilisateur Ă©volutives et distribuĂ©es est complexe ; la complexitĂ© logique, la configuration des tests, les coĂ»ts de build et de dĂ©ploiement augmentent avec le temps. ​Les dĂ©cisions architecturales prises dans la phase initiale peuvent gĂ©nĂ©rer une complexitĂ© inutile et fortement affecter les coĂ»ts de dĂ©veloppement dans les Ă©tapes ultĂ©rieures.​ De plus, un seul projet ne s'adapte pas bien aux Ă©quipes distribuĂ©es travaillant en collaboration sur la mĂȘme base de code.​ Passer Ă  une configuration micro-frontend peut rĂ©soudre tous les problĂšmes ci-dessus ; elle s'adapte bien, les dĂ©ploiements atomiques ne nĂ©cessitent pas de build complet, et les Ă©quipes indĂ©pendantes peuvent utiliser diffĂ©rentes bases de code.​​

# Ce qui définit un micro-frontend

​Les rĂšgles principales qui peuvent dĂ©finir une configuration micro-frontend peuvent ĂȘtre rĂ©sumĂ©es comme suit :​

ResponsabilitĂ© unique FrontiĂšres dĂ©finies et fermĂ©es Orchestration centralisĂ©e​ ResponsabilitĂ© unique ​Chaque application micro-frontend ne devrait fournir que des fonctionnalitĂ©s mĂ©tier spĂ©cifiques. Une application micro-frontend n'a pas besoin de connaĂźtre d'autres aspects de l'activitĂ© et peut Ă©voluer indĂ©pendamment.​

FrontiĂšres dĂ©finies et fermĂ©es ​Chaque micro-frontend devrait ĂȘtre isolĂ©, possĂ©der ses propres donnĂ©es, et la communication directe entre micro-frontends ne devrait pas ĂȘtre possible.​

Orchestration centralisĂ©e ​Chaque micro-frontend devrait ĂȘtre chargĂ©, gĂ©rĂ© et contrĂŽlĂ© par un hĂŽte. Les fonctionnalitĂ©s Ă  l'Ă©chelle de l'application sont fournies par l'hĂŽte (authentification, routage, etc.).​​

# Types de configurations de micro-frontends

​Il existe plusieurs façons d'implĂ©menter des micro-frontends, pour en citer quelques-unes :​

  • Composition par Iframe
  • Composition Ă  l'exĂ©cution
  • Composition par fĂ©dĂ©ration de modules (framework unique)​

Composition par Iframe ​La composition par Iframe est probablement la façon la plus ancienne et la plus facile d'implĂ©menter des micro-frontends, grĂące Ă  l'ancien support HTML pour les iframes et l'isolation de contexte native qu'elle offre. La communication entre l'hĂŽte et les micro-frontends est gĂ©nĂ©ralement difficile Ă  rĂ©aliser et ne s'adapte pas bien au web moderne.​

Composition Ă  l'exĂ©cution ​La composition Ă  l'exĂ©cution est l'idĂ©e de charger dynamiquement des scripts JS situĂ©s sur des URLs http/https et de composer le rĂ©sultat localement. ​Bien qu'elle vous permette thĂ©oriquement d'utiliser des technologies indĂ©pendantes pour chaque micro-frontend, elle est Ă©galement trĂšs difficile Ă  maintenir en raison des diffĂ©rences entre les frameworks utilisĂ©s dans les micro-frontends.​​

Composition SPA ​La fĂ©dĂ©ration de modules est une technologie implĂ©mentĂ©e dans Webpack 5 qui vous permet de charger dynamiquement des modules distants au moment de l'exĂ©cution. CombinĂ©e avec un framework d'application unique (par exemple React), elle permet aux applications construites d'ĂȘtre divisĂ©es en plusieurs micro-frontends sans sacrifier les avantages qu'un SPA offre. Elle prĂ©sente Ă©galement l'avantage de tailles de build plus petites.​​

# La configuration choisie

Nous avons choisi d'utiliser la composition SPA avec Webpack 5 et React. Il vaut la peine de mentionner qu'afin de construire un SPA avec plusieurs micro-frontends, un contrat spĂ©cifique et rigoureux entre l'hĂŽte et les frontends doit ĂȘtre implĂ©mentĂ© et respectĂ©.​ DorĂ©navant nous ferons rĂ©fĂ©rence aux micro-frontends dans la forme technique utilisĂ©e par Webpack 5 : les remotes. ​Le contrat est dĂ©fini par les rĂšgles suivantes :​

  • L'hĂŽte rĂ©cupĂšre la liste des remotes dynamiquement et de maniĂšre asynchrone
  • L'hĂŽte est responsable du chargement des remotes
  • L'hĂŽte partage un certain contexte avec les remotes (routage, authentification)
  • Les remotes ont des noms uniques
  • Les remotes sont dĂ©ployĂ©s sur diffĂ©rentes URLs
  • Les remotes n'utilisent pas de rĂšgles CSS globales
  • Les remotes s'exportent eux-mĂȘmes comme dĂ©fini par les rĂšgles de fĂ©dĂ©ration de modules
  • Les remotes partagent la mĂȘme version de React (et de certaines bibliothĂšques)​

Lorsque ces rĂšgles sont respectĂ©es, il n'y a pratiquement aucune limite Ă  la façon dont le SPA peut croĂźtre.​ La plupart des dĂ©pendances de base utilisĂ©es dans chaque frontend sont fournies par l'hĂŽte. Cela facilite leur mise Ă  niveau. ​Chaque application est construite indĂ©pendamment des autres ; le pipeline CI/CD reste rapide, les dĂ©ploiements atomiques ne nĂ©cessitent pas de configurations complexes et chaque remote est publiĂ© Ă  son propre rythme sans avoir besoin de modifier l'hĂŽte de quelque façon que ce soit.​

# Exemple en direct hébergé sur un CDN

Consultez l'exemple en direct suivant : https://microfrontend-shell-boilerplate.vercel.app/ (opens new window)

# DépÎts Git

Voici une liste de dépÎts Git qui font partie de cette implémentation :