Des monolithes à l'ombre du micro

Partager par e-mail

Les microservices émergent aujourd'hui pour la simple raison que les applications monolithiques sont complexes à maintenir, à faire évoluer et à administrer. Il était donc nécessaire de segmenter notre monolithe en une multitude de fragments. Ces fragments permettent une évolution technique et/ou fonctionnelle beaucoup plus simple.
Cette évolution est désormais courante dans le domaine du backend. Cependant, le problème du monolithe n'est pas exclusivement réservé au backend, on le retrouve également dans les technologies frontend. Les SPA sont devenus de plus en plus répandus à l'aide de frameworks tels que Angular, React, ou même Vue, mais ils restent des monolithes. Bien qu'une multitude d'outils au sein des frameworks nous permettent d'améliorer l'architecture et d'optimiser la segmentation, le code reste mutualisé. Il est donc nécessaire, pour se rapprocher au maximum des microservices, de segmenter et d'évoluer vers des micro-frontends.


Les microservices sont apparus dans les architectures backend dans le but de segmenter le code en une multitude d'applications autonomes et indépendantes. Cette approche permet d'améliorer l'évolutivité et la maintenabilité d'une application.
Dans le domaine du Frontend, les pratiques vont dans le même sens avec l'utilisation de modules et de librairies qui peuvent être réutilisés dans toute l'application. Cette segmentation présente des avantages tant sur le plan technique qu'organisationnel. En effet, cette pratique donne à une équipe la possibilité de gérer une activité logique de A à Z. De la gestion de la base de données, en passant par l'exposition des données (Microservice), jusqu'à la mise en place d'une interface graphique (Frontend). Ainsi, l'évolution d'une fonctionnalité particulière serait maîtrisée par une seule équipe qui se concentrerait sur l'intégration de cette fonctionnalité et non sur l'intégration de cette fonctionnalité dans une architecture donnée.
Actuellement, si l'on étudie toutes les solutions disponibles sur le marché, on tombe rapidement sur les technologies de composants Web avec Webpack 5 et l'utilisation de la "Fédération de modules". Ces pratiques semblent être les plus appropriées pour s'en tenir à une approche micro-frontend, tant sur le plan technique que sur celui de l'expérience utilisateur.

Évolution de Monolith à Microservices

Comme nous pouvons le constater aujourd'hui sur nos gammes de produits, le développement d'applications se faisait au travers d'une architecture monolithique qui était composée de toutes les briques d'une application (la couche d'accès à la base de données, la logique métier, et la présentation).
Comme mentionné ci-dessus, l'architecture d'une application a évolué ou plutôt a été segmentée afin d'optimiser sa maintenance et son évolution. La figure 1 ci-dessous met en évidence cette évolution et guide notre réflexion pour les Microfrontends.

Figure 1. Evolution des architectures applicatives

Monolithe

Toutes les briques qui composent une application sont situées dans la même application. L'accès à la base de données, la logique métier et l'interface utilisateur sont regroupés. Lors de la maintenance ou du développement, toutes les briques doivent être redéployées.

Architecture Backend / Frontend

La segmentation du backend et du frontend est apparue avec l'arrivée des "xml http requests" qui ont permis la manipulation et la communication de données en utilisant le protocole http. Le frontend est désormais autonome et le backend est toujours composé de la logique métier/technique et de l'accès aux données.

Architecture Microservice

La segmentation du monolithe en backend avec l'utilisation des Microservices a permis de définir une application par domaine métier et ainsi de mieux répartir les charges sur les équipes de développement (un domaine associé à une équipe, par exemple). On se retrouve donc avec un ensemble d'applications autonomes et indépendantes capables de fournir un seul service associé à sa logique métier. Avec ce type d'architecture, on voit apparaître des API Gateways permettant de rediriger et de sécuriser les requêtes entrantes.

Et le monolithe à l'avant ?

Enfin, lorsque nous regardons de plus près notre architecture Microservice, nous remarquons que cette segmentation n'a été faite que sur le backend. En effet, le backend bénéficie d'une bonne segmentation en domaines d'activité, expose une API en conséquence et devient complètement indépendant. Néanmoins, l'application frontale reste très cohérente. Le problème du monolithe n'a donc été que partiellement résolu. Ce problème est d'autant plus visible sur les grandes applications avec une scalabilité qui se passe plutôt bien sur le backend mais qui est plus problématique sur le frontend (obligation de reprendre et redéployer toute l'application sur le frontend).
Des problèmes techniques apparaissent :

  • Une évolution d'un point de vue fonctionnel sur le backend équivaut à instancier une nouvelle application (Microservice) mais sur le frontend, cela revient à ajouter une fonctionnalité parmi d'autres. Cela signifie que l'on finit par déployer une application de plus en plus conséquente (risque de plantage plus élevé).
  • La migration fait partie intégrante de notre travail aujourd'hui, il est nécessaire de penser aux utilisations futures et à la possibilité d'obsolescence des technologies actuelles. Plus notre code/architecture est compact et étroitement lié, plus l'évolution sera complexe.
  • Le problème suivant découle directement du précédent avec la difficulté de trouver des développeurs dans quelques années, lorsque la technologie sera obsolète.

Comme pour le back-end, nous avons dû trouver une architecture et un moyen de découpler notre application front-end en plusieurs domaines indépendants et réutilisables. Cela garantirait une meilleure maintenabilité et une meilleure intégration dans différentes applications.
La componentialisation a permis de cloisonner des fonctionnalités qui remplissaient une seule et même fonction et ce indépendamment de l'environnement dans lequel elles étaient intégrées. Le développement serait assuré par une seule équipe de développement responsable de l'ensemble de la chaîne métier (API Microfrontend). Ceci nous amène à un autre avantage, qui est la possibilité de modulariser une application en fonction du client. En effet, si chaque composant métier est autonome, indépendant, et peut être intégré dans n'importe quel environnement, il est tout à fait possible d'agréger ces composants pour en faire une application.

Figure 2. Évolution vers une architecture micro-frontale

Enfin, du point de vue de l'utilisateur, l'application sera une agrégation de composants partagés, gérant chacun un domaine métier particulier. Le "conteneur" sera donc le garant de l'orchestration de tous ces composants (voir figure 3).

Figure 3. Architecture micro-frontale du point de vue de l'interface utilisateur

Avoir la possibilité de diviser notre frontend en plusieurs Microfrontend nous conduit de plus en plus vers une division des équipes de développement par domaine d'activité. Cela permettrait à l'équipe d'être en charge de la logique métier de A à Z (du Microservice au Microfrontend). Enfin, les intégrateurs d'applications frontales n'auront plus besoin de connaître parfaitement le domaine du Microservice. Ils devront seulement intégrer le composant créé par l'équipe Microservice.
Lorsque le microservice évolue, le composant adapté sera développé en même temps par la même équipe (mais probablement par des experts techniques différents) et sera donc prêt à être intégré rapidement par toutes les applications clientes.

Avantages et inconvénients

Bien que le cœur du problème soit la difficulté d'évolution et de maintenance au sein d'un frontend monolithique, il a également été constaté qu'en développement, les équipes en bout de chaîne devaient connaître tous les Microservices avec leurs contraintes, restrictions etc....
Désormais, les équipes sont organisées autour de domaines d'activité et prennent en charge le développement de bout en bout d'un service. L'avantage est qu'elles n'ont pas à se soucier de l'environnement dans lequel l'ensemble sera intégré. Elles peuvent se concentrer uniquement sur leurs domaines d'activité. Quant aux équipes d'intégration, leur seul but est d'intégrer une application avec une multitude de Microfrontends. Elles n'ont plus à se soucier des problématiques métiers. Enfin, de manière plus générale, cela rend une équipe plus spécialisée et lui évite d'être généraliste.
L'un des inconvénients que l'on peut noter avec ce type d'architecture est la nécessité de disposer de connaissances techniques plus larges. En effet, chaque équipe métier sera responsable du développement à la fois du Microfrontend et du Microservice.

En pratique...

Le web d'aujourd'hui nous offre un nombre énorme d'outils pour créer nos applications. Cependant, le choix d'une technologie ou d'un framework se fait principalement en fonction du besoin, du délai de livraison, voire du budget. Lorsque nous parlons de Microfrontend, nous distinguons deux catégories de solutions : les applications qui vont partager le même environnement d'exécution et celles qui ne le font pas.
La grande majorité des applications frontales qui sont développées aujourd'hui sont des applications qui donnent un environnement d'exécution partagé afin de garantir le meilleur résultat pour le développeur mais aussi pour l'utilisateur.

Environnement d'exécution séparé

Les applications qui suivent ce modèle ont la possibilité de garantir une séparation claire et précise du reste de l'application. Bien qu'elle soit bénéfique d'un côté, elle présente des inconvénients, notamment en ce qui concerne la communication avec le monde extérieur. Néanmoins, du point de vue de l'utilisateur, le Microfrontend apparaîtra comme une brique (plus ou moins intégrée) de l'application.

Iframes

C'est probablement le concept le plus connu de tous. Les iframes permettent d'exécuter une application web à l'intérieur d'une autre. L'utilisateur ne voit qu'une seule page, mais la séparation technique est là, trop là.
Cet outil a, comme mentionné précédemment, la capacité de faire cohabiter plusieurs pages/applications mais apporte de nombreux inconvénients. La communication des données au sein d'une application est un critère important pour qu'une interface soit la plus "conviviale" possible. Dans le cas d'un iFrame, la communication inter-frames est plus que limitée et l'intégration de plusieurs Iframes reste superficielle du point de vue de l'utilisateur. Le dernier inconvénient qui pourrait être le plus important est la gestion des CORS qui devient complexe.
Enfin, l'iFrame est un outil plutôt démodé de nos jours et qui répond à de vieux problèmes. Cette approche est utilisée pour une intégration purement statique d'une page, complètement étanche à son environnement d'exécution.

Micro Apps

Dans le cas des micro applications, la séparation est moins nette que pour les iFrames, mais reste présente avec l'utilisation du routage applicatif pour servir un Microfrontend ou un autre. Dans ce cas, on s'approche d'une segmentation par domaine d'activité. Exemple :

Cependant, cette pratique implique la mise en place d'un hub permettant de proposer à l'utilisateur et de le rediriger vers le bon Microfrontend. Cela permet de se rapprocher au maximum des microservices avec une segmentation par domaine métier mais nécessite une bonne gestion du transfert d'identité (authentification, préférences, etc.). De plus, la communication entre les micro-frontends et leur connaissance mutuelle reste compliquée.
Bien que cette solution soit la plus proche de notre vision des micro-frontaux, elle est loin d'être satisfaisante aujourd'hui. Notamment du point de vue de l'utilisateur, qui souffrira d'une expérience utilisateur quelque peu confuse.

Environnement d'exécution partagé

Lorsque notre application offre un environnement d'exécution partagé, toutes les ressources sont disponibles pour tous les composants de l'application. Nous parlons alors de Microfrontend comme de composants de taille variable qui sont intégrés dans une même application. Cette segmentation par composant est aussi visible du côté utilisateur que du côté technique.

Composants web

Les composants Web sont des blocs de code qui englobent la structure interne des éléments HTML, y compris CSS et JavaScript, et permettent d'utiliser le code concerné à l'endroit souhaité dans les sites et les applications Web.
Ceux-ci peuvent être intégrés dans une application de la même manière que les blocs courants proposés par HTML (div, input, etc.). Avec les composants Web, nous avons la possibilité de créer des briques HTML/CSS/JS qui remplissent une fonction précise et peuvent être intégrées dans n'importe quelle application.
Le web est aujourd'hui un domaine qui évolue très rapidement, de nouveaux frameworks et librairies sortent chaque mois et il est difficile de faire un choix dans cette masse d'outils. Les composants web arrivent sur le marché pour garantir la pérennité de nos développements avec l'encapsulation JS/CSS et le support W3C. La communication entre les composants est simplifiée par le système des "events". L'idée ici n'est pas de détailler le fonctionnement des composants web mais l'harmonie entre les composants web est régie par une "base app" qui orchestre le tout.
Nous disposons d'une véritable solution qui est durable et qui nous permet d'optimiser notre application. Cependant, un micro-frontend ne peut être réduit à un composant. Il est associé à un domaine métier et donc à une logique métier, à des appels REST, à des transformations de données, à plusieurs interfaces, etc. Il serait réducteur de dire qu'un micro-frontend est un composant. Ce problème a été résolu par les frameworks javascript.

Framework Javascript

Les frameworks Javascript nous permettent de créer des composants plus complexes avec des services, intégrés dans un module qui sont eux-mêmes associés à un domaine d'activité. Nous pouvons citer par exemple Angular, React, ou VueJS pour les plus connus. Ces frameworks nous permettent de segmenter notre application en une multitude de modules correspondant aux domaines métiers des microservices.
Notez que les frameworks modernes utilisent la technologie des composants Web. Pour le partage de composants complexes, c'est la solution la plus efficace, tant au niveau du développement que de l'UX. Il est possible d'intégrer différents modules de manière transparente pour l'utilisateur et de gérer des flux de travail afin de le guider dans ses tâches.
A titre indicatif, il est possible de faire cohabiter plusieurs SPA de cadres différents en utilisant un outil tel que "SingleSpa".

Webpack 5 : Fédération des modules

Webpack est un regroupeur de modules statiques bien établi pour les applications JavaScript modernes. Il compte plus de 57 000 étoiles sur GitHub et plus de 13 millions de téléchargements hebdomadaires depuis le registre npm. Il a publié la version 5 le 10 octobre 2020, qui apporte un certain nombre de changements, notamment une nouvelle fonctionnalité : Module Federation.
La fédération de modules permet à plusieurs composants web (construits à l'aide de webpack) de fonctionner ensemble. Une application peut exécuter dynamiquement du code provenant d'un autre bundle ou build. C'est un renouveau pour le monde des micro-frontends.
Chaque build webpack peut être un hôte, qui est un conteneur pour charger d'autres builds. Il peut aussi être vu comme un remote, qui est un micro-frontend à charger. Chaque application peut être un remote et un host, un consumable et un consumer de tout autre module fédéré dans le système.
En outre, la Fédération des modules ne doit pas nécessairement charger le point d'entrée principal ou une autre application entière. Elle ne doit charger que le code nécessaire, soit quelques kilo-octets de code.
Ainsi, chaque application a la possibilité de travailler de manière autonome et indépendante car elle est avant tout une application à part entière mais elle est également capable d'être packagée et intégrée dans n'importe quelle autre application.
Cette approche permet de référencer des parties du programme qui ne sont pas encore connues au moment de la compilation. Il peut s'agir de micro-frontaux auto-compilés. De plus, un des points importants de la fédération de modules est la possibilité de partager des bibliothèques entre les différents micro-frontends. Cela permet de réduire considérablement la taille de nos bundles et de notre application entière.

Application

Le shell de notre application peut être considéré comme le point d'entrée ou l'orchestrateur de tous les micro-frontaux intégrés dans notre application. Il est en fin de compte l'hôte et les micro-frontaux sont les remotes. La fédération de modules permet (figure 4) de partager des bibliothèques entre plusieurs micro-frontaux. Nous pouvons distinguer quatre applications Angular partageant la même pile de bibliothèques, dont Angular.
Avec cette approche, nous pouvons constater qu'il est facile de faire cohabiter plusieurs micro-frontends issus de différents frameworks. En effet, Webpack5 se charge de packager nos différents remotes en composants Web, ce qui rend leur utilisation beaucoup plus simple.
En effet, un remote peut également être un hôte en hébergeant d'autres micro-frontends (figure 4).
Dans la figure ci-dessous, nous pouvons voir un mécanisme de routage permettant de charger les différents micro-frontends. Là encore, il est possible de les charger dynamiquement avec le lazy loading afin de réduire au maximum le bundle lors du chargement de la page.

Figure 4. Logique des micro-frontaux selon la fédération de modules Webpack 5

Webpack 5 nous permet de proposer clairement une solution pour les micro-frontends en couplant les composants Web au partage de bibliothèques. Notons qu'Angular ou React sont des frameworks javascript qui intègrent déjà Webpack dans leur pipeline de packaging. La fédération de modules est donc une fonctionnalité disponible dans ces deux frameworks.

Conclusion

Avec les frameworks JavaScript tels que React et Angular, les développeurs peuvent définir des éléments web réutilisables. Cependant, ces frameworks utilisent une autre norme qui, dans de nombreux cas, empêche la réutilisation de bouts de code pratiques dans différents projets. Les composants Web, des éléments HTML réutilisables et indépendants du framework, permettent de remédier à cette situation.
Cependant, comme nous l'avons vu, l'utilisation de composants web bruts ne permet pas de proposer des composants complexes avec une réelle logique métier derrière. L'arrivée de Webpack5 avec des fonctionnalités telles que la fédération de modules permet de coupler les composants Web et l'utilisation de frameworks javascript.
Enfin, la meilleure granularité que les outils actuels peuvent offrir pour avoir une architecture micro-frontale est l'utilisation de frameworks javascript couplés à Webpack5 afin de mieux gérer l'utilisation des composants Web.

Plus ...

Retour en haut