Architecture de micro-applications : Un gain de temps garanti

Partager par e-mail

Afin de garantir une meilleure maintenabilité et évolutivité de nos applications, nous sommes souvent amenés à décomposer notre application en une multitude de micro-services ayant leur propre logique. Ces micro-services doivent être aussi autonomes que possible et remplir une fonction spécifique afin de pouvoir les réutiliser dans différents contextes.
Cette architecture est énormément mise en œuvre dans le développement côté Backend et donc plus mature. On note par exemple l'émergence de certaines librairies de registre, API-gateway qui permettent de supporter une telle architecture. En revanche, il est plus difficile d'architecturer son application FrontEnd (Angulaire, Reactou VueJS) dans des micro-services et encore moins pour le mettre en œuvre dans le monde mobile qui, par définition, est monolithique.
L'objectif de cet article est de montrer qu'il est possible de modulariser une application mobile, dans notre cas sous... Flutteret surtout comment le construire à partir de plusieurs micro-applications.

Vision globale

Le développement d'une application modulaire revient à faire le parallèle avec un assemblage de différents blocs, chacun remplissant des fonctions bien spécifiques et ayant le moins de dépendances extérieures possibles. Dans notre cas d'étude, cette modularité est présente afin de disposer de plusieurs variantes d'une même application et donc de pouvoir en un minimum de temps présenter la même application avec des fonctionnalités différentes.
Cette architecture de micro-application va donc permettre d'une part de présenter une multitude de variantes d'une application et d'autre part de permettre un développement plus robuste. En effet, une telle architecture permettra d'optimiser la maintenance et l'évolutivité de l'application. La maintenance de l'application ne s'effectuera que sur la micro-application défaillante ou évolutive et l'évolutivité de l'application reviendra à ajouter une micro-application dans l'application. Par conséquent, le déploiement ou la mise à jour de fonctionnalité ne se fera que sur une partie de l'application limitant au maximum une régression applicative (Défaut introduit dans le logiciel à l'occasion de corrections de bugs ou de tout changement établi).
Plus concrètement, les micro-applications permettent de développer en utilisant des "référentiels" différents pour chaque micro-application. Ce dernier est dans la plupart des cas orienté autour des fonctionnalités métier. Cela permet également à des équipes distinctes de travailler sur chaque micro-application.

Avant de se lancer dans la mise en œuvre de ce type d'architecture en FlutterDans le cadre de cette étude, prenons un peu de recul et analysons les capacités des plates-formes croisées face au développement natif. Cette partie évaluera les performances des différents frameworks multiplateformes selon plusieurs critères (Productivité, maintenabilité...). Nous verrons, par la suite, une comparaison basée sur les mêmes critères et mettant en opposition les architectures micro-applicatives et monolithiques.

Plates-formes croisées VS Native

Les frameworks multiplateformes permettent aux développeurs de créer des applications mobiles compatibles avec plusieurs systèmes d'exploitation, en l'occurrence iOS et Android. Ils donnent la possibilité d'écrire le code une fois et de l'exécuter n'importe où sur d'autres plateformes, ce qui leur permet de publier des produits/logiciels plus rapidement, de manière plus sûre et avec une meilleure qualité. Mais voyons si les performances sont au rendez-vous.
Le développement d'applications natives évite la complexité de la création d'un produit durable qui couvre le développement d'applications sur plusieurs plateformes et se concentre sur la création d'une application compétente qui reste proche de la plateforme cible - Android, iOS, etc.
Les cadres multiplateformes, quant à eux, cherchent à générer une application qui touche le plus grand nombre de personnes possible en couvrant de nombreux appareils pendant le processus de programmation et de création.

CritèresNativeMultiplateformes
CoûtCoût de développement élevéCoût de développement relativement faible
Réutilisation du codeFonctionne pour une seule plateformeUn seul code peut être utilisé sur plusieurs plateformes, ce qui facilite la portabilité.
Accès aux appareils (caméra, etc.)Le SDK (Software Development Kit) garantit l'accès à l'API de l'appareil sans aucun obstacle.Pas d'accès garanti à toutes les API des appareils
Cohérence de l'interface utilisateurConforme aux composants de l'interface utilisateur du dispositifCohérence limitée avec les composants de l'interface utilisateur du dispositif
PerformanceDes performances fluides, car l'application est développée pour le système d'exploitation des appareils.Performances élevées, mais les retards et les problèmes de compatibilité matérielle ne sont pas rares.

La question du choix d'un développement natif ou multiplateforme repose en grande partie sur les besoins que vous avez et sur le projet en question.
Enfin, les avantages d'une application native sont :

  • Haute performance
  • Une fonctionnalité robuste
  • Une expérience utilisateur fluide

Et celle d'une application multiplateforme :

  • 70 à 90% code réutilisable
  • Maintenance et mises à jour faciles
  • Une portée plus large
  • Délai de commercialisation plus court

Cependant, au fil des années, une multitude de cadres ont vu le jour, et nous pouvons en distinguer deux types :
Applications natives multiplateformes: Chaque système d'exploitation possède son SDK (Software Development Kit) et sa pile technologique : Java ou Kotlin pour Android et Objective-C ou Swift pour les applications iOS. Les développeurs d'applications multiplateformes créent une API unifiée fonctionnant sur un SDK natif, utilisent des IDE natifs et créent des applications iOS et Android qui partagent la même base de code. Les applications multiplateformes natives sont généralement créées avec les outils suivants Xamarin, React Nativeet Flutter.
Applications hybrides: Comme les SDK iOS et Android comprennent des composants Web avancés, il est possible de créer des parties de l'interface utilisateur graphique (GUI) d'une application avec HTML5, CSS et JavaScript. Ensuite, les développeurs enveloppent le code dans une "WebView" - un navigateur intégré à une application mobile, qui rend le contenu comme un bon vieux site Web. Certaines applications hybrides interagissent même avec le matériel du smartphone, bien que leur fonctionnalité puisse être limitée. Les cadres de développement d'applications hybrides les plus prometteurs actuellement sur le marché sont les suivants Apache Cordova et Ionic.
Les applications natives multiplateformes telles que Flutter est arrivé après les applications hybrides afin de surmonter le manque de performance avec l'utilisation de Webview et l'utilisation de widgets natifs d'un point de vue UI / UX.

Productivité

La productivité lors du développement d'une application peut être mesurée en fonction de plusieurs points :
La courbe d'apprentissage : la capacité d'un nouveau développeur sur le Framework a développé des fonctionnalités plus complexes et plus rapides.
Réutilisation du code : Le code produit par le développeur est-il exportable ou peut-il être utilisé sur différentes plateformes ?

La courbe d'apprentissage semble être de plus en plus élevée à mesure que l'on se rapproche du développement natif. En effet, il est plutôt facile d'utiliser React Nativesurtout si vous avez déjà utilisé React ou Javascriptil est très proche du développement web avec le concept de composant. Si nous nous dirigeons vers un Framework plus proche du matériel comme Flutteron constate que l'apprentissage est assez rapide mais qu'il est nécessaire d'apprendre Dart, et que la programmation réactive n'est pas totalement intuitive. Enfin, le développement natif tel que IOS avec Objectif-C ou Android avec Kotlin reste un langage complexe et dense à apprendre. Cependant, les possibilités sont beaucoup plus nombreuses, tant sur le plan technique que fonctionnel.
En ce qui concerne la capacité à réutiliser le code, les frameworks multiplateformes ont clairement le dessus avec la possibilité de produire plusieurs applications avec un seul code. En effet, le développement natif oblige le développeur à écrire la même fonctionnalité sur plusieurs plateformes.

Utilisation des ressources matérielles

Le développement d'applications natives offre un accès à toutes les fonctions matérielles de l'appareil cible (Appareil photo, géolocalisation, etc.). Cependant, les applications multiplateformes ont accès à la plupart de ces fonctionnalités soit par le biais d'un pont, soit par le biais du navigateur (pour Webview). Je pense à la géolocalisation, à la caméra, ou encore au microphone. L'accès aux ressources matérielles n'est pas vraiment une faiblesse des applications multiplateformes. Des communautés grandissantes facilitent même leur accès de manière native ou par l'ajout de plugins. Pour une utilisation classique des fonctions matérielles, les deux solutions sont au même niveau.

Source ouverte, propriétaire, sous licence (coût)

L'avantage des frameworks multiplateformes les plus connus tels que Flutter ou React Native est qu'ils sont open source et gratuits. Elles ne nécessitent pas d'avoir un environnement particulier pour être utilisées contrairement au développement IOS par exemple. En effet, le choix d'opter pour des applications multiplateformes est en partie dû au coût du développement IOS. Ce dernier est plutôt restrictif et nous oblige à disposer d'un environnement spécifique qui augmente le coût du développement.

Maintenabilité

La maintenance des logiciels peut être évaluée selon deux critères plus précis :

  • Entretien évolutif: La capacité de modifier ou de corriger des erreurs plus ou moins facilement / rapidement.
  • Obsolescence: La technologie utilisée a-t-elle un avenir ? Est-elle soutenue par une communauté importante ?

Si l'on adopte une vision purement logique, la maintenance évolutive d'une application qui serait sur deux plateformes (Android / IOS) prendrait deux fois plus de temps sur le développement natif que sur les développements multiplateformes.
Du point de vue de l'obsolescence, qu'il s'agisse de plates-formes croisées avec des systèmes natifs ou de l'utilisation d'un système de gestion de l'information. Flutter et React ou dans les versions natives d'Android et d'Ios, les chances que ces frameworks disparaissent restent minces. En effet, Flutter est soutenu par Google et sa communauté s'agrandit de jour en jour, React Native est maintenu par Facebook et dispose d'une grande communauté héritée notamment de React. En outre, Android et IOS sont les systèmes d'exploitation les plus populaires dans le monde.
D'autre part, les frameworks tels que Ionic basés principalement sur Webview deviennent de plus en plus obsolètes du point de vue des performances.
Enfin, le choix de l'environnement de développement de votre application dépendra des besoins et exigences de cette dernière et des capacités des développeurs. Si les équipes sont issues du monde du WEB, le passage au React Native ne portera pas à confusion. Flutter reste aujourd'hui la solution multiplateforme la plus proche du matériel mais nécessite l'apprentissage d'un langage assez nouveau et peu performant, Dart. Enfin, pour produire une application de qualité et performante en développement natif, la courbe d'apprentissage reste élevée.

La figure ci-dessus résume les avantages et les inconvénients des différentes technologies que nous avons pu aborder sur la base de critères de performance, d'accès aux équipements et de coûts.

Architecture monolithique VS micro-application

Le choix de l'architecture dépend, là encore, des besoins et des exigences du projet mais aussi du personnel qui travaillera sur l'application. Cependant, nous allons nous baser sur des critères (similaires à la partie précédente) nous permettant d'évaluer les avantages et les inconvénients de chacun.

Productivité

Ces dernières années, les usages et les pratiques ont évolué vers des architectures de micro-applications. Cependant, un passé monolithique reste présent dans nos façons de développer des applications et la courbe d'apprentissage pour amener les développeurs à comprendre et utiliser une telle architecture n'est pas facile. Nous verrons plus loin les différents avantages d'une architecture de micro-applications, mais il est intéressant de noter un avantage étroitement lié à la productivité (développement) : La possibilité de se concentrer sur la fonctionnalité à développer et non sur le mode d'intégration dans la future application. De ce point de vue, une telle architecture permet à un développeur de gagner grandement en productivité.
Lors du développement dans une architecture monolithique, il est dans la plupart des cas nécessaire de connaître l'environnement dans lequel la fonctionnalité est intégrée et d'avoir une connaissance de la logique métier de l'application. Dans le cas d'une architecture micro-applicative, le champ d'application est restreint et donc la formation d'un nouveau développeur pour intégrer le développement d'une application est plus courte avec une architecture micro-applicative.
De plus, cette capacité à créer une brique autonome et indépendante, et non une fonctionnalité de plus dans une application, facilite et optimise la réutilisation dans une multitude d'applications. Bien sûr, les architectures monolithiques permettent de réutiliser des modules, des composants, voire des services, tout en restant dans les limites de l'application.
Enfin, ce qui impacte le plus la productivité avec l'architecture micro-applicative est l'installation et le " câblage " des différentes briques. En effet, il sera nécessaire de dédier une équipe / un développeur à la mise en place du point central de l'application (l'orchestrateur). Mais le temps perdu dans cette mise en œuvre n'est-il pas gagné lors de l'implémentation de nouvelles fonctionnalités ?

Utilisation des ressources matérielles

Sur ce point, l'adoption d'une architecture ou d'une autre n'a pas d'impact sur les ressources matérielles disponibles.

Maintenabilité

Là encore, une architecture micro-applicative présente des avantages non négligeables lors de la maintenance d'une application. On peut noter par exemple la possibilité de cibler une erreur/bug plus facilement que dans un monolithe. En effet, la séparation de l'application étant plus nette avec une microarchitecture, le problème est rapidement identifiable. De plus, la mise en place d'une batterie de tests spécifiques à la micro-application simplifie l'utilisation des tests et réduit le risque de régression lors d'une mise à jour.
En rejoignant l'idée ci-dessus, l'évolution d'une application suivant une architecture de micro-application est grandement simplifiée. En effet, l'implémentation d'une nouvelle fonctionnalité revient à créer une nouvelle brique autonome et indépendante (micro-application) avec des entrées et sorties bien définies. Le processus d'évolution dans une architecture monolithique nécessite une contextualisation afin de pouvoir implémenter de nouvelles fonctionnalités et surtout où.

Contexte et cas d'utilisation

La valeur ajoutée d'une architecture de micro-applications est la possibilité de modulariser une application en une multitude de micro-applications. Cette modularisation présente plusieurs avantages.

Figure 1 - Diagramme mettant en évidence la valeur ajoutée des "micro" architectures

Actuellement, les micro-services sont de plus en plus populaires car ils permettent de répartir les coûts au niveau du développement mais aussi au niveau technique. Pour les micro-applications destinées aux mobiles ou aux tablettes, la véritable valeur ajoutée réside dans le plan de développement avec la possibilité d'associer une logique métier à une équipe de développement (du micro-service côté backend à l'application côté frontend). Techniquement, les applications pour mobiles ou tablettes seront dans tous les cas un monolithe en fin de cycle.

En mettant en place une architecture de micro-applications, la possibilité de produire une application avec différentes variations devient plus simple. En effet, cette segmentation permet de modulariser notre application et de la rendre personnalisable.

Ce type d'architecture n'est pas adapté à tous les cas d'utilisation et surtout à toutes les applications. Pour que cette architecture apporte une réelle valeur ajoutée, l'application doit être de grande taille et impliquer un nombre important de développeurs.

Architecture choisie

L'infrastructure d'une application, les bases de données, les appels API, ainsi que les frameworks, évoluent constamment. Mais ce n'est pas forcément le cas pour le besoin métier. En partant de ce principe, il est plus judicieux d'orienter notre manière de segmenter notre application à travers le métier.
L'architecture hexagonale créée par Alistair Cockburn assure la réutilisation de la logique métier en la rendant agnostique aux techniques. Ainsi, la modification de la pile n'aura aucun impact sur le code du domaine. Un concept clé de cette architecture est de placer toute la logique métier en un seul endroit appelé le domaine.

Figure 2 - Principe d'une architecture hexagonale

L'architecture hexagonale peut être la solution car elle tend à isoler une application en trois couches distinctes :

Cette couche contient les entités existantes dans le domaine. Dans cette couche, nous ne nous soucions pas de la manière d'exposer les données à l'utilisateur ni de la manière dont elles seront stockées. Les entrées/sorties sont effectuées en utilisant uniquement des interfaces. Pour ce faire, il faut garder à l'esprit que l'entreprise ne doit dépendre de rien et donc ne jamais être liée à la couche Application ou à la couche Infrastructure.

C'est dans cette couche que l'Infrastructure va communiquer avec le Domaine par le biais des interfaces. On considère ici que la couche Application connaît le Domaine mais ne doit jamais être liée à l'Infrastructure. C'est ici que, pour des raisons métiers, on peut enrichir, comparer et filtrer les éléments retournés par les interfaces du Domaine sans se soucier de l'implémentation côté Infrastructure. Il est courant de parler dans cette couche de Handler.

L'architecture hexagonale considère que le domaine est au centre de l'application. En fait, la couche infrastructure va pouvoir communiquer et invoquer les objets du Domaine à travers les interfaces. Attention, l'infrastructure connaît la couche Application mais pas l'inverse ! De cette manière, si vous voulez changer des API tierces, faire de la double écriture ou changer le modèle de données, il suffit de recréer un adaptateur dédié dans la couche Infrastructure tout en respectant l'interface Domaine.

Cette architecture est principalement axée sur un domaine d'activité particulier. Or, dans notre cas, nous souhaitons réunir une multitude de domaines métiers (micro-applications) et pouvoir, à partir de ces micro-applications, créer une seule application. Cette capacité à faire résider plusieurs micro-applications dans une seule repose sur un orchestrateur. Un point d'entrée pour intégrer toutes nos micro-applications. C'est d'ailleurs un élément important qui revient assez fréquemment dans la segmentation de nos applications. Que ce soit pour les micro-services avec le système API-gateway ou les containers (docker) avec docker-compose ou Kubernetes, il y a toujours un orchestrateur, un point d'entrée pour gérer toutes les briques.

Mise en œuvre

Le POC réalisé était donc basé sur le concept d'architecture hexagonale. Bien que l'architecture hexagonale permette une isolation optimale du métier, elle ne nous permet pas de segmenter notre application à grande échelle. En effet, cette architecture intervient sur un composant métier spécifique et non sur l'ensemble des différentes logiques métier.
Lorsque l'on parle de segmentation, il est toujours important de penser à la manière d'orchestrer toutes ces parties segmentées. Dans notre cas, l'application est segmentée en une multitude de paquets, et l'orchestrateur est représenté par le shell (En bleu dans la figure ci-dessous). Cependant, nous pouvons noter plusieurs types de paquets :

  • Micro-application commerciale : Il s'agit d'une interface graphique à part entière. Elle remplit une fonction métier spécifique et son point d'entrée est un widget de haut niveau. Ces packages respectent une architecture hexagonale. Ce sont ces packages qui peuvent être considérés comme des micro-applications car ils manipulent des données métier (En orange dans la figure ci-dessous). Ils font partie de la logique métier.
  • Micro-application fonctionnelle : C'est un widget comme un bouton, une entrée ... réutilisable dans l'ensemble de l'application ainsi que dans tous les packages. Ce sont des interfaces utilisateurs transversales à l'application et en général à toute application. Ces packages ne comprennent pas de données métier mais uniquement des données de présentation. Ils font partie de la logique métier.
  • Micro-application technique : C'est une micro-application qui est présente pour la logique technique. On retrouve notamment la gestion de la persistance des données et la gestion des types. On peut noter que ces packages n'ont pas d'interface utilisateur. Ils font partie de la logique de l'infrastructure.
Figure 3 - Schéma de l'architecture de la micro-application sous Flutter

La coquille

Il représente le point d'entrée de notre application et nous permet de configurer l'application en fonction de plusieurs entrées. En effet, le shell récupère plusieurs informations de l'environnement pour construire l'application spécifique. Il y a trois fichiers en particulier :

  • Le fichier de configuration contient toutes les variantes disponibles de l'application. Ce fichier est le résultat d'une étude de plus haut niveau afin de déterminer les différentes compatibilités entre les paquets et les variantes qui sont capables d'être un constructeur. A savoir qu'une variante est composée d'une description des fonctionnalités et des paquets spécifiques à la variante.
  • Le fichier de dépendances est également construit en amont avec le fichier de configuration afin d'avoir la possibilité de choisir les versions des paquets utilisés dans la variante choisie.
  • Les variables d'environnement interviennent juste avant la construction de l'application afin de spécifier un certain nombre de variables fonctionnelles ou techniques. Exemple : L'identifiant de la variante ou l'url du serveur ...

Gestion du routage

Comme nous l'avons dit précédemment, la segmentation implique une réunification à un moment ou à un autre. Si nous prenons l'exemple des micro-services, la réunification se fait dans la plupart des cas par le biais d'une passerelle API afin de fournir un point d'entrée unique à notre API. Dans notre cas, la réunification implique l'utilisation du routage avec le chargement dynamique des interfaces métier (micro-applications graphiques) en utilisant des URL.

Gestion de la configuration

L'une des exigences qui ont été mises en avant pour justifier l'utilisation d'une architecture de micro-applications est la possibilité de moduler une application en plusieurs variantes. En effet, cette architecture nous permet de "construire" une application différente/personnalisée pour chaque utilisateur.
Cette variante de construction implique l'utilisation de variables d'environnement. Celles-ci nous permettent de donner des paramètres d'entrée au moment de la construction de notre application. A partir de ces variables, nous sommes donc en mesure de construire notre application avec une certaine configuration.
Avant la construction et l'utilisation de ces variables d'environnement, un outil supplémentaire de génération de code est utilisé pour générer le fichier de dépendance et une classe de configuration. Ces deux dernières générations proviennent de l'outil JSON fichier de configuration décrivant de manière statique les micro-applications qui seront chargées dans l'application par le biais de fonctionnalités.
Actuellement, ce fichier est créé manuellement mais cela implique de nombreux verrous techniques et fonctionnels. En effet, on note par exemple la manipulation des dépendances des différentes versions ou la compatibilité d'un point de vue fonctionnel entre différentes micro-applications. Ceci nous amène à la conclusion qu'il est nécessaire de faire intervenir un outil de plus haut niveau pour gérer la génération du fichier de configuration (JSON décrivant de manière statique les fonctionnalités des différentes variantes).
Ce dossier prend en compte plusieurs éléments :

  • Toutes les caractéristiques disponibles
  • La définition des micro-applications associées aux fonctionnalités

En suivant ces générations, notre shell contient une classe de configuration représentant toutes les routes disponibles dans notre application finale.

Partage de l'information (contextualisation)

Cependant, notre application est, comme nous l'avons vu, segmentée en une multitude de micro-applications qui n'ont aucune idée du contexte dans lequel elles sont utilisées. Il était donc nécessaire de partager cette configuration à travers l'ensemble de nos packages. Ainsi, tout package a la possibilité de faire référence à un package de configuration qui contient la configuration de notre application.
La valeur ajoutée de ce package est donc de donner un contexte aux micro-apps. Exemple : Une micro-application pourrait visualiser, modifier, supprimer des données. Cependant, la configuration nous informe que la variante chargée ne permet pas la suppression. Le package de configuration nous permet d'accéder à cette information n'importe où dans notre application et surtout dans n'importe quelle micro-app. Même si cette dernière se trouve loin dans l'arbre de navigation. Cela nous évite de passer la configuration à chaque parent/enfant (voir le schéma ci-dessous : Figure 3).

Figure 4 - Différence entre les méthodes d'injection de dépendances

La gestion des types/modèles au sein de l'application se fait en deux parties. On note les modèles métiers et les modèles techniques. Pour mieux gérer les DTOs, les modèles associés à une logique métier sont placés dans la micro_app concernée. Par contre, les modèles plus techniques et purement associés à la gestion de l'application sont fournis dans un package séparé gérant la configuration de notre application.
Ainsi, chaque paquet a la possibilité d'ajouter un contexte et un lien entre nos paquets.

Champ d'application des différents paquets

Lors de la création d'une micro-application associée à un domaine métier ou à un sous-domaine métier (opération particulière sur un domaine métier par exemple), on peut voir cette dernière comme une coquille vide qui sera alimentée en interne par la logique métier (manipulation de données propres au métier par exemple). De plus, des paquets externes indépendants pourront apporter des applications ou des fonctionnalités techniques supplémentaires à notre logique métier.
Ainsi, comme on peut le voir sur la FIG. 2, on retrouve la logique métier au centre avec une couche applicative permettant de gérer, entre autres, les données. En périphérie, l'utilisation de packages externes permet de délocaliser les interfaces utilisateurs transverses et le code purement technique.

Mise en œuvre de l'intégration continue

L'architecture des micro-applications a été conçue comme la capacité d'être modulaire, d'avoir la possibilité de gérer plusieurs variantes d'une même application. Ce processus de modularisation a été grandement simplifié par l'intégration continue. Le projet présente des points d'entrée tels que la variante choisie ou l'URL de notre backend. Mais on peut très bien imaginer avoir une multitude de variables en entrée. Celles-ci sont utilisées pour définir une classe de configuration qui liste toutes les variables d'entrée. Cette classe est consommée par l'application pour accéder à l'environnement d'exécution de l'application et ainsi fournir l'application appropriée.
Ainsi, avec deux commandes, nous avons la possibilité de choisir par exemple la variante de notre application et d'optimiser le chargement des micro-applications. Ce qui est intéressant à noter ici, c'est cette possibilité de pouvoir faire varier l'application en amont du build. Ceci a été rendu possible grâce à l'utilisation du package "environment_config" et du projet de génération de code. Les variables d'environnement (de décision) lors de l'intégration continue proviennent d'un pipeline d'intégration continue et sont donc facilement modifiables.
Enfin, la mise en place de l'intégration continue a été réalisée à l'aide de la Forge et plus particulièrement de Gitlab CI/CD. Il suffit d'un fichier YAML ". GitLab-ci.yml" pour décrire comment nous allons modulariser notre application et créer notre artefact de sortie. Une fois la nouvelle version de l'application poussée sur une certaine branche (dans notre cas "master"), un script sera lancé.
Comme nous l'avons vu précédemment, dans le cas du choix des variantes, les variables d'environnement ont un caractère décisionnel et non descriptif. En effet, la description de la variante (sa configuration) se fait manuellement dans le code ou bien est générée par un outil de niveau supérieur qui aura la capacité de considérer les exigences fonctionnelles, par exemple. Il est donc recommandé, si l'on devait avoir des configurations un peu complexes, d'utiliser les variables d'environnement uniquement à des fins décisionnelles et de déclarer dans le code ou de récupérer les objets complexes.

Processus de création d'une architecture de micro-application

L'axe principal sur lequel nous devons nous pencher lorsque nous voulons respecter une architecture de micro-application est l'aspect métier. En effet, notre architecture est basée sur une architecture hexagonale (orientée vers le domaine métier). Ainsi, la première étape consiste à identifier les différents composants métier impliqués dans l'application.
Les paquets métier ont la spécificité de traiter et de présenter des données propres à la logique métier. En outre, cette dernière doit être aussi indépendante que possible de la logique métier externe. Occasionnellement, un paquet métier peut avoir besoin d'accéder à des informations provenant d'un autre paquet métier. Le but n'est pas de réimplémenter la manière d'aller chercher un composant métier. Il est nécessaire de passer par le paquetage métier pour récupérer l'information. Cela garantit l'intégrité des données tout en conservant un code métier centralisé.
Il est possible dans une même logique métier de découpler cette dernière en une multitude de paquets métier. En effet, une fois le composant métier identifié, nous pouvons remarquer qu'il peut être séparé en plusieurs fonctionnalités. Nous pouvons identifier, par exemple, un package de visualisation et un package d'édition de la logique métier. Ils peuvent être séparés car ils ne remplissent pas les mêmes fonctions.

Identification des interfaces transversales (fonctionnelles et techniques)

Lors du processus de migration, il est nécessaire d'identifier rapidement le socle technique de l'application et les éléments qui sont réutilisés dans plusieurs ou tous les progiciels métiers. En effet, nous pouvons remarquer dans l'application étudiée que nous avons un " noyau " qui représente la base technique de l'application et plus précisément la configuration de l'application. Cette logique a été placée au niveau du package app_management (package déjà présent dans l'architecture de référence et indispensable pour la gestion des configurations et du routage) car elle concerne la logique applicative et doit être accessible par tous les packages. Vous pouvez également identifier ou créer des éléments (widgets) qui sont répétés dans l'application. On peut citer par exemple les "entrées" avec un certain style, les boutons, etc...

Exemple de cas d'utilisation

Lors du développement d'une application, plusieurs fonctionnalités peuvent être implémentées telles que la géolocalisation, la numérisation des QRCodes, ou la mise en place d'une base de données locale. Pour faciliter l'identification et la conception des paquets, voici quelques exemples de fonctionnalités associées à un type de paquet.

Mise en place d'une base de données

Lorsqu'il est nécessaire pour une application qu'il y ait des données persistantes, une base de données locale doit être mise en place. Nous aurions tendance à instancier une seule base de données commune avec un package qui gérerait cette base de données. Ce paquet serait alors un paquet technique remplissant un rôle très spécifique d'accès et de mise à disposition des données.
Cependant, si on essaie de faire un parallèle avec les micro-services, on peut remarquer que la bonne pratique est d'avoir une base de données par micro-service et donc logiquement par logique métier. Ainsi, la mise en place d'une base de données se fait idéalement dans le package métier associé. Cela permet encore une fois d'avoir une seule interface qui accède et modifie les données.

Géolocalisation

La géolocalisation est une fonction qui peut être utilisée par une multitude de paquets car elle n'implique pas une entreprise dans sa logique. Elle n'a pas besoin du contexte dans lequel elle va rendre l'information à la fonction. Ainsi, nous pouvons implémenter cette fonctionnalité en utilisant un package fonctionnel (fonctionnalité transversale).

Gestion et stockage de la configuration

Il est courant de devoir stocker des données de configuration relatives au fonctionnement de l'application ou aux préférences des utilisateurs. Toute cette gestion doit impérativement se retrouver dans le package gérant les configurations et le routage de notre application. Le package "app_management" est un package essentiel pour le fonctionnement de notre future application et il est possible d'y ajouter vos propres configurations.

Conclusion

La mise en place d'une architecture de micro-applications nous a permis de mettre en avant la possibilité de modulariser ou de personnaliser une application à travers différentes variantes. Et ceci de la manière la plus simple possible avec l'utilisation de l'intégration continue. Le fait de segmenter notre application est de la voir comme un assemblage de briques et non comme un seul bloc, nous permet d'ouvrir des portes sur la possibilité de mettre en place un développement par une équipe métier qui aurait à supporter toute la chaîne (du backend avec son micro-service au front-end avec sa micro-application).
Ainsi, les maintenances et les évolutions de nos produits se feront de manière optimisée et se concentreront sur la fonctionnalité à maintenir ou à faire évoluer. Nous ne nous préoccuperons plus d'intégrer une fonctionnalité dans une application mais de développer une fonctionnalité indépendante et réutilisable dans n'importe quelle application.
Tout cela pour construire une pile de développement commune et mutualisée au sein de Berger-Levrault.

Prêt à commencer avec notre cadre de micro-app, allez à la forge : https://gitlab.forge.berger-levrault.com/bl-drit/flutter-micro-app-architecture

Plus ...

Retour haut de page