Les points forts et les avantages des micro-interfaces
Publié: 2022-03-11L'architecture micro-frontend est une approche de conception dans laquelle une application frontale est décomposée en "microapps" individuelles et semi-indépendantes travaillant ensemble de manière lâche. Le concept de micro-frontend est vaguement inspiré et nommé d'après les microservices.
Les avantages du modèle micro-frontend incluent :
- Les architectures micro-frontend peuvent être plus simples, et donc plus faciles à raisonner et à gérer.
- Les équipes de développement indépendantes peuvent collaborer plus facilement sur une application frontale.
- Ils peuvent fournir un moyen de migrer à partir d'une "ancienne" application en ayant une "nouvelle" application en cours d'exécution côte à côte.
Bien que les micro-frontends aient beaucoup retenu l'attention ces derniers temps, il n'existe pas encore d'implémentation dominante unique ni de "meilleur" cadre de micro-frontend clair. En fait, il existe une variété d'approches en fonction des objectifs et des besoins. Voir la bibliographie pour certaines des implémentations les plus connues.
Dans cet article, nous passerons en revue une grande partie de la théorie des micro-interfaces. Voici ce que nous ne couvrirons pas :
- "Découper" une application en microapps
- Problèmes de déploiement, y compris la manière dont les micro-interfaces s'intègrent dans un modèle CI/CD
- Essai
- Si les microapps doivent être alignées individuellement avec les microservices sur le backend
- Critiques du concept de micro-frontend
- La différence entre les micro-interfaces et une architecture de composant simple et ancienne
Au lieu de cela, nous présenterons un didacticiel micro-frontend axé sur une implémentation concrète, mettant en évidence les problèmes importants de l'architecture micro-frontend et leurs solutions possibles.
Notre implémentation s'appelle Yumcha. Le sens littéral de "yum cha" en cantonais est "boire du thé", mais son sens quotidien est "sortir pour des dim sum". L'idée ici est que les microapps individuelles au sein d'une macroapp (comme nous appellerons l'app composée de haut niveau) sont analogues aux différents paniers de bouchées apportées lors d'un déjeuner dim sum.
Nous ferons parfois référence à Yumcha en tant que "framework micro-frontend". Dans le monde d'aujourd'hui, le terme «framework» est généralement utilisé pour désigner Angular, React, Vue.js ou d'autres superstructures similaires pour les applications Web. Nous ne parlons pas du tout d'un cadre dans ce sens. Nous appelons Yumcha un cadre juste pour des raisons de commodité : il s'agit en fait davantage d'un ensemble d'outils et de quelques couches minces pour créer des applications basées sur des micro-interfaces.
Premiers pas du didacticiel micro-frontend : balisage pour une application composée
Plongeons-nous en réfléchissant à la façon dont nous pourrions définir une macroapp et les microapps qui la composent. Le balisage a toujours été au cœur du Web. Notre macroapp ne sera donc spécifiée par rien de plus compliqué que ce balisage :
<html> <head> <script src="/yumcha.js"></script> </head> <body> <h1>Hello, micro-frontend app.</h1> <!-- HERE ARE THE MICROAPPS! --> <yumcha-portal name="microapp1" src="https://microapp1.example.com"></yumcha-portal> <yumcha-portal name="microapp2" src="https://microapp2.example.com"></yumcha-portal> </body> </html>
Définir notre macroapp à l'aide du balisage nous donne un accès complet à la puissance du HTML et du CSS pour mettre en page et gérer nos microapps. Par exemple, une micro-application peut s'asseoir au-dessus d'une autre, ou sur le côté, ou être dans le coin de la page, ou être dans un volet d'un accordéon, ou rester cachée jusqu'à ce que quelque chose se produise, ou rester en arrière-plan en permanence .
Nous avons nommé l'élément personnalisé utilisé pour les microapps <yumcha-portal>
car « portail » est un terme prometteur pour les microapps utilisé dans la proposition de portail, une première tentative de définition d'un élément HTML standard à utiliser dans les micro-frontends.
Implémentation de l'élément personnalisé <yumcha-portal>
Comment implémenter <yumcha-portal>
? Puisqu'il s'agit d'un élément personnalisé, en tant que composant Web, bien sûr ! Nous pouvons choisir parmi un certain nombre de candidats sérieux pour l'écriture et la compilation de composants Web micro-frontaux ; ici, nous utiliserons LitElement, la dernière itération du projet Polymer. LitElement prend en charge le sucre syntaxique basé sur TypeScript, qui gère pour nous la plupart du passe-partout des éléments personnalisés. Pour rendre <yumcha-portal>
disponible sur notre page, nous devons inclure le code correspondant en tant que <script>
, comme nous l'avons fait ci-dessus.
Mais que fait <yumcha-portal>
? Une première approximation serait qu'il crée simplement une iframe
avec la source spécifiée :
render() { return html`<iframe src=${this.src}></iframe>`; }
… où render
est le crochet de rendu LitElement standard, en utilisant son littéral de modèle balisé html
. Cette fonctionnalité minimale peut être presque suffisante pour certains cas d'utilisation triviaux.
Intégrer des microapps dans des iframe
s
iframe
s sont l'élément HTML que tout le monde aime détester, mais en réalité, ils offrent un comportement de sandbox extrêmement utile et solide comme le roc. Cependant, il reste encore une longue liste de problèmes à prendre en compte lors de l'utilisation d' iframe
s, avec un impact potentiel sur le comportement et la fonctionnalité de notre application :
- Premièrement, les
iframe
s ont des bizarreries bien connues en termes de taille et de disposition. - Le CSS sera bien sûr complètement isolé de l'
iframe
, pour le meilleur ou pour le pire. - Le bouton "retour" du navigateur fonctionnera raisonnablement bien, bien que l'état de navigation actuel de l'
iframe
ne soit pas reflété dans l'URL de la page , nous ne pouvons donc ni couper et coller des URL pour obtenir le même état de l'application composée, ni lien profond pour eux. - La communication avec l'
iframe
depuis l'extérieur, selon notre configuration CORS, peut nécessiter de passer par le protocolepostMessage
. - Des dispositions devront être prises pour l'authentification au-delà des limites de l'
iframe
. - Certains lecteurs d'écran peuvent trébucher sur la limite de l'
iframe
ou avoir besoin que l'iframe
ait un titre qu'ils peuvent annoncer à l'utilisateur.
Certains de ces problèmes peuvent être évités ou atténués en n'utilisant pas d' iframe
s, une alternative dont nous parlerons plus loin dans l'article.
Du côté positif, l' iframe
aura sa propre Content-Security-Policy
(CSP) indépendante. De plus, si la micro-application vers laquelle l' iframe
pointe utilise un agent de service ou implémente le rendu côté serveur, tout fonctionnera comme prévu. Nous pouvons également spécifier diverses options de sandboxing à l' iframe
pour limiter ses capacités, telles que la possibilité de naviguer vers le cadre supérieur.
Certains navigateurs ont fourni ou prévoient de fournir un attribut loading=lazy
pour les iframe
s, qui diffère le chargement des iframe
s en dessous de la ligne de flottaison jusqu'à ce que l'utilisateur fasse défiler près d'eux, mais cela ne fournit pas le contrôle précis du chargement paresseux qui nous voulons.
Le vrai problème avec les iframe
s est que le contenu de l' iframe
nécessitera plusieurs requêtes réseau pour être récupéré. Le index.html
de niveau supérieur est reçu, ses scripts sont chargés et son code HTML est analysé, mais le navigateur doit alors lancer une autre requête pour le code HTML de l' iframe
, attendre de le recevoir, analyser et charger ses scripts et restituer le le contenu de l' iframe
. Dans de nombreux cas, le JavaScript de l' iframe
devrait encore tourner, effectuer ses propres appels d'API et afficher des données significatives uniquement après le retour de ces appels d'API et le traitement des données pour l'affichage.
Cela entraînera probablement des retards indésirables et des artefacts de rendu, en particulier lorsque plusieurs microapps sont impliquées. Si l'application de l' iframe
implémente le SSR, cela aidera mais n'évitera pas la nécessité d'allers-retours supplémentaires.
Ainsi, l'un des principaux défis auxquels nous sommes confrontés lors de la conception de la mise en œuvre de notre portail est de savoir comment gérer ce problème d'aller-retour. Notre objectif est qu'une seule requête réseau fasse tomber la page entière avec toutes ses micro-applications, y compris le contenu que chacune d'entre elles est capable de préremplir. La solution à ce problème réside dans le serveur Yumcha.
Le serveur Yumcha
Un élément clé de la solution micro-frontend présentée ici est de mettre en place un serveur dédié pour gérer la composition des microapps. Ce serveur transmet les requêtes aux serveurs sur lesquels chaque microapp est hébergée. Certes, il faudra un certain effort pour configurer et gérer ce serveur. Certaines approches micro-frontend (par exemple, un seul spa) tentent de se passer de la nécessité de telles configurations de serveur spéciales, au nom de la facilité de déploiement et de configuration.
Cependant, le coût de la mise en place de ce proxy inverse est plus que compensé par les avantages que nous en tirons ; en fait, il existe des comportements importants des applications basées sur des micro-interfaces que nous ne pouvons tout simplement pas réaliser sans elles. Il existe de nombreuses alternatives commerciales et gratuites à la mise en place d'un tel proxy inverse.
Le proxy inverse, en plus d'acheminer les demandes de microapp vers le serveur approprié, achemine également les demandes de macroapp vers un serveur de macroapp. Ce serveur prépare le code HTML de l'application composée d'une manière spéciale. Lors de la réception d'une demande d' index.html
du navigateur via le serveur proxy à une URL telle que http://macroapp.example.com
, il récupère le index.html
puis le soumet à une transformation simple mais cruciale avant de revenir ce.
Plus précisément, le HTML est analysé pour <yumcha-portal>
, ce qui peut être fait facilement avec l'un des analyseurs HTML compétents disponibles dans l'écosystème Node.js. En utilisant l'attribut src
de <yumcha-portal>
, le serveur exécutant la micro-application est contacté et son index.html
est récupéré, y compris le contenu rendu côté serveur, le cas échéant. Le résultat est inséré dans la réponse HTML sous la forme d'une <script>
ou <template>
, afin de ne pas être exécuté par le navigateur.

Les avantages de cette configuration incluent, avant tout, que lors de la toute première demande de index.html
pour la page composée, le serveur peut récupérer les pages individuelles des serveurs de microapp individuels dans leur intégralité, y compris le contenu SSR, si any—et livrer une seule page complète au navigateur, y compris le contenu qui peut être utilisé pour remplir l' iframe
sans allers-retours supplémentaires avec le serveur (en utilisant l'attribut srcdoc
sous-utilisé). Le serveur proxy garantit également que tous les détails sur l'endroit d'où les micro-applications sont servies sont cachés aux regards indiscrets. Enfin, cela simplifie les problèmes de CORS, puisque les demandes d'application sont toutes adressées à la même origine.
De retour au niveau du client, la <yumcha-portal>
est instanciée et trouve le contenu là où il a été placé dans le document de réponse par le serveur, et au moment approprié rend l' iframe
et attribue le contenu à son attribut srcdoc
. Si nous n'utilisons pas d' iframe
s (voir ci-dessous), le contenu correspondant à cette <yumcha-portal>
est inséré soit dans le shadow DOM de l'élément personnalisé, si nous l'utilisons, soit directement en ligne dans le document.
À ce stade, nous avons déjà une application micro frontale partiellement fonctionnelle.
Ce n'est que la pointe de l'iceberg en termes de fonctionnalités intéressantes pour le serveur Yumcha. Par exemple, nous voudrions ajouter des fonctionnalités pour contrôler la façon dont les réponses d'erreur HTTP des serveurs de microapp sont gérées, ou comment traiter les microapps qui répondent très lentement. Nous ne voulons pas attendre indéfiniment pour servir la page si une microapp n'est pas répondre ! Nous laisserons ces sujets et d'autres pour un autre article.
La logique de transformation Yumcha macroapp index.html
pourrait facilement être implémentée à la manière d'une fonction lambda sans serveur, ou en tant que middleware pour des frameworks de serveur tels qu'Express ou Koa.
Contrôle de microapp basé sur le stub
Pour en revenir au côté client, il y a un autre aspect de la façon dont nous implémentons les micro-applications qui est important pour l'efficacité, le chargement paresseux et le rendu sans à-coups. Nous pourrions générer la balise iframe
pour chaque microapp, soit avec un attribut src
- qui fait une autre requête réseau - soit avec l'attribut srcdoc
rempli avec le contenu rempli pour nous par le serveur. Mais dans ces deux cas, le code de cet iframe
immédiatement, y compris le chargement de tous ses scripts et balises de lien, l'amorçage et tous les appels d'API initiaux et le traitement des données associées, même si l'utilisateur n'accède même jamais à la micro-application en question.
Notre solution à ce problème consiste à représenter initialement les microapps sur la page comme de minuscules stubs inactivés, qui peuvent ensuite être activés. L'activation peut être pilotée soit par l'apparition de la région de la micro-application, en utilisant l'API IntersectionObserver
sous-utilisée, soit plus communément par des pré-notifications envoyées de l'extérieur. Bien sûr, nous pouvons également spécifier que la microapp soit activée immédiatement.
Dans tous les cas, lorsque et seulement lorsque la microapp est activée, l' iframe
est réellement rendu et son code chargé et exécuté. En termes de notre implémentation utilisant LitElement, et en supposant que le statut d'activation est représenté par une variable d'instance activated
, nous aurions quelque chose comme :
render() { if (!this.activated) return html`{this.placeholder}`; else return html` <iframe srcdoc="${this.content}" @load="${this.markLoaded}"></iframe>`; }
Communication inter-microapp
Bien que les microapps composant une macroapp soient par définition faiblement couplées, elles doivent tout de même pouvoir communiquer entre elles. Par exemple, une micro-application de navigation devrait envoyer une notification indiquant qu'une autre micro-application que l'utilisateur vient de sélectionner doit être activée, et l'application à activer doit recevoir ces notifications.
Conformément à notre état d'esprit minimaliste, nous voulons éviter d'introduire de nombreuses machines de transmission de messages. Au lieu de cela, dans l'esprit des composants Web, nous utiliserons des événements DOM. Nous fournissons une API de diffusion triviale qui pré-notifie tous les stubs d'un événement imminent, attend tous ceux qui ont demandé à être activés pour que ce type d'événement soit activé, puis distribue l'événement par rapport au document, sur lequel n'importe quelle micro-application peut écouter ce. Étant donné que tous nos iframe
sont de même origine, nous pouvons tendre la main de l' iframe
à la page et vice-versa pour trouver des éléments sur lesquels déclencher des événements.
Routage
De nos jours, nous nous attendons tous à ce que la barre d'URL dans les SPA représente l'état d'affichage de l'application, nous pouvons donc couper, coller, envoyer, envoyer du texte et créer un lien pour accéder directement à une page de l'application. Dans une application micro-frontend, cependant, l'état de l'application est en fait une combinaison d'états, un pour chaque micro-application. Comment représenter et contrôler cela ?
La solution consiste à encoder l'état de chaque microapp dans une seule URL composite et à utiliser un petit routeur macroapp qui sait comment assembler cette URL composite et la séparer. Malheureusement, cela nécessite une logique spécifique à Yumcha dans chaque microapp : pour recevoir des messages du routeur macroapp et mettre à jour l'état de la microapp, et inversement pour informer le routeur macroapp des changements dans cet état afin que l'URL composite puisse être mise à jour. Par exemple, on pourrait imaginer un YumchaLocationStrategy
pour Angular, ou un élément <YumchaRouter>
pour React.
Le cas non- iframe
Comme mentionné ci-dessus, l'hébergement de microapps dans des iframe
présente certains inconvénients. Il existe deux alternatives : les inclure directement en ligne dans le code HTML de la page ou les placer dans le DOM fantôme. Les deux alternatives reflètent quelque peu les avantages et les inconvénients des iframe
s, mais parfois de différentes manières.
Par exemple, les politiques CSP de microapp individuelles devraient être fusionnées d'une manière ou d'une autre. Les technologies d'assistance telles que les lecteurs d'écran devraient mieux fonctionner qu'avec les iframe
s, en supposant qu'elles prennent en charge le DOM fantôme (ce qui n'est pas encore le cas pour tous). Il devrait être simple d'organiser l'enregistrement des techniciens de service d'une micro-application en utilisant le concept de "portée" des techniciens de service, bien que l'application doive s'assurer que son technicien de service est enregistré sous le nom de l'application, et non "/"
. Aucun des problèmes de mise en page associés à iframe
ne s'applique aux méthodes DOM inline ou shadow.
Cependant, les applications construites à l'aide de frameworks tels que Angular et React risquent d'être malheureuses de vivre en ligne ou dans le DOM fantôme. Pour ceux-ci, nous allons probablement vouloir utiliser des iframe
s.
Les méthodes DOM inline et shadow diffèrent en matière de CSS. CSS sera proprement encapsulé dans le shadow DOM. Si, pour une raison quelconque, nous voulions partager en dehors du CSS avec le DOM fantôme, nous devions utiliser des feuilles de style constructibles ou quelque chose de similaire. Avec les micro-applications intégrées, tous les CSS seraient partagés sur toute la page.
En fin de compte, la mise en œuvre de la logique pour les micro-applications DOM en ligne et fantômes dans <yumcha-portal>
est simple. Nous récupérons le contenu d'une micro-application donnée à partir de laquelle il a été inséré dans la page par la logique du serveur en tant qu'élément HTML <template>
, le clonons, puis l'ajoutons à ce que LitElement appelle renderRoot
, qui est normalement le DOM fantôme de l'élément, mais peut également être défini sur l'élément lui-même ( this
) pour le cas en ligne (DOM non fantôme).
Mais attendez! Le contenu servi par le serveur de microapp est une page HTML entière. Nous ne pouvons pas insérer la page HTML de la microapp, avec les balises html
, head
et body
, au milieu de celle de la macroapp, n'est-ce pas ?
Nous résolvons ce problème en tirant parti d'une bizarrerie de la balise de template
dans laquelle le contenu de la microapp récupéré depuis le serveur de la microapp est enveloppé. Il s'avère que lorsque les navigateurs modernes rencontrent une balise de template
, bien qu'ils ne l'exécutent pas, ils l' analysent et, ce faisant, ils suppriment le contenu invalide tel que les balises <html>
, <head>
et <body>
, tout en préservant leur contenu intérieur. Ainsi, les balises <script>
et <link>
dans le <head>
, ainsi que le contenu du <body>
, sont préservées. C'est précisément ce que nous voulons pour insérer du contenu de microapp dans notre page.
Architecture micro-frontend : le diable est dans les détails
Les micro-interfaces prendront racine dans l'écosystème des applications Web si (a) elles s'avèrent être une meilleure approche architecturale, et (b) nous pouvons trouver comment les mettre en œuvre de manière à satisfaire la myriade d'exigences pratiques du Web d'aujourd'hui.
En ce qui concerne la première question, personne ne prétend que les micro-interfaces sont la bonne architecture pour tous les cas d'utilisation. En particulier, il y aurait peu de raisons pour qu'une seule équipe adopte des micro-interfaces. Je laisserai la question de savoir quels types d'applications dans quels types de contextes pourraient bénéficier le plus d'un modèle de micro-frontend à d'autres commentateurs.
En termes de mise en œuvre et de faisabilité, nous avons vu qu'il y a de nombreux détails à prendre en compte, dont plusieurs ne sont même pas mentionnés dans cet article, notamment l'authentification et la sécurité, la duplication de code et le référencement. Néanmoins, j'espère que cet article présente une approche de mise en œuvre de base pour les micro-interfaces qui, avec un raffinement supplémentaire, peut répondre aux exigences du monde réel.
Bibliographie
- Micro frontaux - Doing It Angular Style - Partie 1
- Micro-frontaux - Doing It Angular Style - Partie 2
- Faire évoluer une application AngularJS à l'aide de microfrontends
- Micro-interfaces
- Microservices d'interface utilisateur - inversion de l'anti-modèle (micro-interfaces)
- Microservices d'interface utilisateur - un anti-modèle ?
- La création de pages à l'aide de micro-frontends adopte une approche similaire à Yumcha du proxy inverse et des SSI, que je recommande vivement.
- Ressources micro-interfaces
- Podium
- Je ne comprends pas les micro-frontends. Il s'agit d'un assez bon aperçu des types d'architectures micro-frontend et des cas d'utilisation.
- Micro-interfaces sans serveur utilisant Vue.js, AWS Lambda et Hypernova
- Micro Frontends : un excellent aperçu complet.