MACH alliance, gagnent en popularité. Ce style d’architecture vise à concevoir des systèmes composables, en prônant 4 principes fondamentaux :
Mais pourquoi diantre en parler en 2024 ? Si ces principes fondamentaux existent depuis longtemps (POO ~1980, SOA ~2000, API REST ~2000-2010, microservices ~2014, cloud computing ~2016), nous assistons aujourd'hui à une stabilisation des pratiques, des principes de conception, des technologies et des écosystèmes sur la base de ces quatres principes fondamentaux qui ont fait leurs preuves.
Si nous ne prônons pas à tout prix la MACH alliance, l'acronyme « MACH », qui a le vent en poupe, a le mérite de poser une vision d’architecture dont l'ambition est d’être la plus « future-proof » possible en bâtissant des systèmes composables, évolutifs et scalables. En outre, nous savons à quel point il est important de définir une vision d’architecture, pour aligner l’ensemble des acteurs tech autour de principes directeurs, afin de donner un cadre de cohérence d’architecture. Il ne faut cependant pas en faire une loi d'airain, et se laisser une liberté dans les choix architecturaux en fonction des besoins qui émergent et savoir sortir du cadre quand cela est nécessaire (voir Architecture pragmatique).
Détaillons maintenant les quatres caractéristiques fondamentales de la MACH Architecture.
Les microservices sont des composants applicatifs autonomes sur un périmètre fonctionnel délimité, mettant en jeu des silos verticaux sur trois couches :
Les microservices sont pertinents dans les contextes de fort besoin de modularité et/ou, d’indépendance des équipes. Le découpage d'une application en modules de taille plus petite (les microservices) permet en théorie de faciliter l'organisation et le fonctionnement des équipes. Ainsi, une équipe responsable d'un ou plusieurs microservices est totalement autonome sur son périmètre et peut développer et déployer son ou ses composant(s) de manière indépendante.
L’acronyme « API » pour Application Programming Interface désigne une interface normalisée par laquelle une application offre des services à une autre application.
Aujourd'hui, les entreprises souhaitent partager proposer un accès à leur système d'information via une API. Dans ce contexte, le terme API désigne en fait une Web API, c'est-à-dire la couche d’exposition d’un système sur l'internet, via le protocole HTTPS (en pratique, souvent des API REST ou GraphQL).
Le développement d’une API porte donc essentiellement sur la logique d’exposition des ressources métiers et le système de persistance sous-jacent. Cela comprend la conception des requêtes, des réponses et des formats de données. Une fois l’API définie, les développeurs peuvent créer des applications qui utilisent les ressources exposées. On parle de mashup quand les applications finales reposent sur la composition de plusieurs ressources provenant éventuellement de systèmes disjoints. L'interface utilisateur peut ainsi être développée par des équipes dédiées, expertes dans l'implémentation des parcours utilisateurs et de problématiques telles que l'accessibilité et l'ergonomie.
Le pattern « API-first »implique de commencer par bâtir une API, pour chaque fonctionnalité du système, puis la consommer y compris en interne, aussi bien par des applications back que pour construire les UI destinées aux utilisateurs finaux (souvent des applications web ou mobiles).
Dans une optique d'amélioration continue, l'idée est d'être soit-même utilisateur des services exposés, afin de vivre la même expérience que les clients consommateurs d'APIs, selon le pattern « Eat Your Own Dog's Food », et ce, afin de réduire la boucle de feedback.
En particulier, avec une approche « API-first » :
Pour aller plus loin sur la thématiques des API, vous pouvez approfondir trois sujets clés :
Une application cloud-native est une application conçue spécifiquement pour être déployée et exécutée dans un environnement cloud, en tirant pleinement parti des services managés et des avantages offerts par le cloud computing — scalabilité, résilience, élasticité, automatisation des déploiements et doit respecter les twelve-factor app.
On identifie d’autres caractéristiques clés des applications cloud-native :
Néanmoins, si l’accélérateur cloud et ses services managés sur étagère sont devenus une nécessité dans de nombreux cas pour répondre aux enjeux d’agilité, d’accélération du time to market et d__'innovation__, il convient d’aborder le sujet move to cloud de manière holistique, en intégrant notamment les aspects :
La caractéristique la moins galvaudée de l'acronyme MACH est probablement l'aspect Headless !
« Le Headless, c’est couper la tête ! »
L’architecture Headless consiste à séparer la couche de présentation de la couche business et contenu, qui est exposée via des API omnicanales et agnostiques du terminal utilisateur.
Un chantier « Going headless » consiste donc à passer d’un système monolithique à un écosystème « best of breed » pour permettre de livrer de manière indépendante les composants front et back, et d'offrir des expériences premium aux utilisateurs sur l’ensemble des touchpoints et canaux digitaux.
Comme abordé à la section MACH Architecture : One architecture to rule them all ?, il est important de rester pragmatique et de se laisser une liberté dans les choix techniques en fonction des besoins qui émergent et savoir sortir du cadre quand cela est nécessaire.
Appliquer sans discernement les principes des architectures MACH et sans tenir compte du contexte peut être contre-productif et amener à des solutions trop complexes (ou trop tôt) au regard des besoins réels.
« Si le seul outil que vous avez est un marteau, vous tendez à voir tout problème comme un clou ». — Abraham Maslow
Un des principes fondamental lié à cette approche pragmatique est de prendre des décisions au moment où c'est nécessaire :
Démarrer avec un monolithe, quitte à le découper plus tard, est une approche pragmatique. Martin Fowler l’a constaté : les architectures microservices qui ont réussi sont souvent parties d’un monolithe : https://martinfowler.com/bliki/MonolithFirst.html
Partir d’un monolithe permet de repousser la définition des périmètres des microservices à un moment où notre expérience sur le fonctionnement réel du système permettra de déterminer les domaines pour lesquels un microservice apporte plus de valeur que la complexité qu’il génère.
En outre, le monolithe permet dans un premier temps de se concentrer sur le fait d’apporter de la valeur métier et d’aller vite, pour par la suite, être découpé en services supportant les différents domaines métier.
Partir d’un monolithe certes, mais un monolithe modulaire, évolutif : l’apprentissage de l’usage est crucial pour savoir comment modulariser son monolithe avec un design émergent. Préparer le découpage en séparant les zones de responsabilités entre les différentes couches métiers mais aussi techniques. On parle alors de « monolithe modulaire » ou de « majestic monolith ».
Il faut se souvenir que les architectures microservices fonctionnent bien dès lors que les frontières sont stables entre les services. Toute refactorisation de fonctionnalités entre services est beaucoup plus difficile que dans un monolithe. Même les experts sur un domaine métier maîtrisé ont des difficultés à fixer des limites dès le départ du projet. D’où l’idée de démarrer avec un monolithe modulaire et ensuite aller vers des architectures microservices si besoin.
Pour rappel, les microservices constituent une architecture utile, avec pour conséquence une entropie du SI qui augmente, ainsi qu’une complexité et des coûts accrus.
Comparaison de la complexité entre un Monolith et une architecture Microservices
(Sources : https://martinfowler.com/bliki/MicroservicePremium.html)
Enfin, il est possible que certains composants du système n'aient en définitive pas besoin de la scalabilité ou de l'indépendance offertes par les microservices, et dans ce cas, on pourrait choisir de conserver ces composants dans une architecture monolithique.
Lorsque l’on découpe son système en sous-systèmes, la question de la granularité est primordiale, et nos retours d’expérience ont démontré qu’il fallait à tout prix éviter un hyper-découpage par services/ressources avec une granularité trop fine. Imaginez un instant un système découpé en 1000 microservices exposant chacun une route (endpoint API) avec sa base de données dédiée. 🤯
Il y a donc une notion de « bonne granularité » pour le découpage en microservices, qui est souvent plus celle d’un découpage par domaines métier (DDD), avec une API documentée dont le versionning garantit aux utilisateurs que les appels entre les différents endpoints fonctionnent. Il ne faut donc pas se focaliser sur le terme “micro” de “microservices” et plutôt se focaliser sur un découpage en services autonomes et cohérents, sans présupposer de leur taille qui oriente le découpage, celui-ci devant être plus métier que technique.
“In general, microservices should cleanly align to bounded contexts.”
— Sam Newman, Building Microservices
Le DDD (https://www.octo.academy/catalogue/formation/ddd01-ddd-domain-driven-design/) propose des outils pour identifier et délimiter les domaines métiers. Les Bounded Contexts (BC) sont idéaux pour définir les domaines dans les modèles de CleanArchi ou Archi hexagonale. Un BC représente une partie de la solution, correspondant à un ou plusieurs domaines métier, porte une responsabilité spécifique, délimité par des frontières explicites. Un Bounded Context contient des modèles purement internes ainsi que des modèles ou fonctionnalités partagées avec l’extérieur (son interface). Ce type de découpage apporte à la fois un couplage faible car chaque BC expose une interface (et non son modèle interne) et une cohésion métier car les BC épousent les contours des domaines métiers.
Il convient donc de rester pragmatique dans l’approche de découpage d’un système en sous-systèmes.
Par ailleurs, dans la mise en œuvre d’une architecture MACH, il arrive que la mise en œuvre de composants API-first ne soit pas la solution la plus adaptée et qu’une architecture événementielle soit plus pertinente sur certains composants.
Pour essayer de démêler le pour et le contre de la gestion par événement ou par appel d’API, on peut analyser comment gérer le besoin de transactions distribuées dans une architecture à base de microservices.
Le pattern SAGA est vu comme la solution au défi posé par les transactions distribuées. Ce pattern décompose une transaction en plusieurs petites transactions gérables. SAGA ne maintient pas les propriétés ACID sur une grande transaction sur plusieurs systèmes techniques. Chaque petite transaction respecte les propriétés ACID, et le pattern SAGA assure la cohérence globale de l’opération via un mécanisme de gestion de statut et de transactions de compensation au sein d’un workflow.
Il y a deux manières de coordonner une SAGA : l’orchestration et la chorégraphie.
Le premier, l’orchestrateur gère l'enchaînement du workflow qui peut comporter du parallélisme. Le deuxième, dans la chorégraphie, chaque service émet un événement pour déclencher la suite du workflow. Les événements peuvent être traités par un ou plusieurs services.
L’orchestrateur comme le chorégraphe peuvent avoir des logiques “séquentielles” ou “parallèles”.
Il y a deux manières de coordonner une SAGA : l’orchestration et la chorégraphie.
Ok mais so what? On y vient !
L’orchestrateur permet de faire des appels “synchrone” (appels d’API par exemple). L’orchestration, consiste en un enchaînement d’actions orchestrés par un coordinateur central qui va contacter dans l’ordre les différents services par appels synchrones (API vers A et B) ou par messages asynchrones (ici vers C).
En chorégraphie : les systèmes applicatifs (A, B, C) se débrouillent entre eux; chaque service s'exécute indépendamment en fonction des événements émis par les autres services (publiés dans un bus ou envoyés directement). Pour une transaction distribuée, chaque service publie des événements en retour et doit prévoir un mécanisme de compensation si une étape assurée par un autre service se termine de manière anormale.
Enfin, concernant les approches Event-driven, avec la popularisation de la démarche event storming, nous avons également rencontré des cas où les événements étaient utilisés de manière systématique pour tous les échanges entre composants, y compris en local sous prétexte que cela réduisait le couplage ! Hors cela entraînait une complexité inutile. Là aussi il convient de rester pragmatique en employant le pattern d’architecture adapté au besoin.
En 2024, on assiste à l’essor des architectures MACH. Ce style d’architecture a précisément l’ambition de concevoir des systèmes composables et propose une vision d’architecture basée sur quatre patterns d’architecture éprouvés : Microservices, API-first, Cloud-native et Headless.
Cette architecture peut apporter des bénéfices et donner un cap, si l’on sait rester pragmatique en sachant éviter le piège d’un micro-découpage et en se laissant la liberté de sortir du cadre quand cela est pertinent.
Croquerez-vous dans la BIG MACH Architecture ?
Pour aller plus loin sur les Tech Trends : téléchargez OCTO Pulse, la publication qui prend le pouls de la tech !