Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Motif Strangler Fig
Intention
Le modèle Strangler Fig permet de migrer progressivement une application monolithique vers une architecture de microservices, tout en réduisant les risques de transformation et les interruptions d'activité.
Motivation
Les applications monolithiques sont développées pour fournir la plupart de leurs fonctionnalités dans un seul processus ou conteneur. Le code est étroitement lié. Par conséquent, les modifications apportées à l'application nécessitent de nouveaux tests approfondis afin d'éviter les problèmes de régression. Les modifications ne peuvent pas être testées isolément, ce qui a un impact sur le temps de cycle. Au fur et à mesure que l'application est enrichie de nouvelles fonctionnalités, une complexité élevée peut entraîner une augmentation du temps consacré à la maintenance, une augmentation du délai de mise sur le marché et, par conséquent, un ralentissement de l'innovation en matière de produits.
Lorsque la taille de l'application augmente, elle augmente la charge cognitive de l'équipe et peut entraîner des limites floues en matière de propriété de l'équipe. Il n'est pas possible de dimensionner les fonctionnalités individuelles en fonction de la charge : l'application entière doit être dimensionnée pour supporter les pics de charge. À mesure que les systèmes vieillissent, la technologie peut devenir obsolète, ce qui augmente les coûts de support. Les anciennes applications monolithiques suivent les meilleures pratiques disponibles au moment du développement et n'ont pas été conçues pour être distribuées.
Lorsqu'une application monolithique est migrée vers une architecture de microservices, elle peut être divisée en composants plus petits. Ces composants peuvent évoluer indépendamment, peuvent être publiés indépendamment et peuvent être détenus par des équipes individuelles. Cela se traduit par une vitesse de changement plus élevée, car les modifications sont localisées et peuvent être testées et publiées rapidement. Les modifications ont un impact moindre car les composants sont mal couplés et peuvent être déployés individuellement.
Remplacer complètement un monolithe par une application de microservices en réécrivant ou en refactorisant le code est une entreprise colossale et un gros risque. Une migration à grande échelle, où le monolithe est migré en une seule opération, entraîne un risque de transformation et perturbe l'activité. Pendant le remaniement de l'application, il est extrêmement difficile, voire impossible, d'ajouter de nouvelles fonctionnalités.
Une façon de résoudre ce problème consiste à utiliser le motif Strangler Fig, introduit par Martin Fowler. Ce modèle implique de passer aux microservices en extrayant progressivement les fonctionnalités et en créant une nouvelle application autour du système existant. Les fonctionnalités du monolithe sont progressivement remplacées par des microservices, et les utilisateurs de l'application peuvent utiliser progressivement les fonctionnalités nouvellement migrées. Lorsque toutes les fonctionnalités sont transférées vers le nouveau système, l'application monolithique peut être mise hors service en toute sécurité.
Applicabilité
Utilisez le motif Strangler Fig lorsque :
-
Vous souhaitez migrer progressivement votre application monolithique vers une architecture de microservices.
-
Une approche de migration à grande échelle est risquée en raison de la taille et de la complexité du monolithe.
-
L'entreprise souhaite ajouter de nouvelles fonctionnalités et est impatiente que la transformation soit terminée.
-
Les utilisateurs finaux doivent être le moins impactés possible lors de la transformation.
Problèmes et considérations
-
Accès à la base de code : pour implémenter le modèle Strangler Fig, vous devez avoir accès à la base de code de l'application Monolith. Au fur et à mesure que les fonctionnalités sont migrées hors du monolithe, vous devrez apporter des modifications mineures au code et implémenter une couche anticorruption au sein du monolithe pour acheminer les appels vers de nouveaux microservices. Vous ne pouvez pas intercepter les appels sans accès par code. L'accès à la base de code est également essentiel pour rediriger les demandes entrantes : une certaine refactorisation du code peut être nécessaire afin que la couche proxy puisse intercepter les appels aux fonctionnalités migrées et les acheminer vers des microservices.
-
Domaine peu clair : La décomposition prématurée des systèmes peut s'avérer coûteuse, en particulier lorsque le domaine n'est pas clair, et il est possible de se tromper dans les limites des services. La conception axée sur le domaine (DDD) est un mécanisme permettant de comprendre le domaine, et le storming d'événements est une technique permettant de déterminer les limites du domaine.
-
Identification des microservices : vous pouvez utiliser DDD comme outil clé pour identifier les microservices. Pour identifier les microservices, recherchez les divisions naturelles entre les classes de services. De nombreux services posséderont leur propre objet d'accès aux données et se découpleront facilement. Les services qui ont une logique métier associée et les classes qui n'ont pas ou peu de dépendances sont de bons candidats pour les microservices. Vous pouvez refactoriser le code avant de décomposer le monolithe pour éviter un couplage serré. Vous devez également tenir compte des exigences de conformité, de la cadence de publication, de la situation géographique des équipes, des besoins en matière de mise à l'échelle, des besoins technologiques axés sur les cas d'utilisation et de la charge cognitive des équipes.
-
Couche anticorruption : pendant le processus de migration, lorsque les fonctionnalités du monolithe doivent appeler les fonctionnalités migrées en tant que microservices, vous devez implémenter une couche anticorruption (ACL) qui achemine chaque appel vers le microservice approprié. Afin de découpler et d'empêcher toute modification des appelants existants au sein du monolithe, l'ACL fonctionne comme un adaptateur ou une façade qui convertit les appels vers la nouvelle interface. Ceci est décrit en détail dans la section Implémentation du modèle ACL plus haut dans ce guide.
-
Défaillance de la couche proxy : pendant la migration, une couche proxy intercepte les demandes envoyées à l'application monolithique et les achemine vers le système existant ou le nouveau système. Cependant, cette couche proxy peut devenir un point de défaillance unique ou un goulot d'étranglement des performances.
-
Complexité de l'application : les grands monolithes sont ceux qui tirent le meilleur parti du motif Strangler Fig. Pour les petites applications, où la complexité du refactoring complet est faible, il peut être plus efficace de réécrire l'application dans une architecture de microservices plutôt que de la migrer.
-
Interactions entre les services : les microservices peuvent communiquer de manière synchrone ou asynchrone. Lorsqu'une communication synchrone est requise, déterminez si les délais d'attente peuvent entraîner une consommation de connexion ou de pool de threads, entraînant des problèmes de performances de l'application. Dans de tels cas, utilisez le schéma du disjoncteur pour annuler immédiatement une panne pour les opérations susceptibles d'échouer pendant de longues périodes. La communication asynchrone peut être réalisée à l'aide d'événements et de files d'attente de messagerie.
-
Agrégation de données : dans une architecture de microservices, les données sont réparties entre les bases de données. Lorsque l'agrégation des données est requise, vous pouvez utiliser AWS AppSync
le modèle frontal ou le modèle de ségrégation des responsabilités des requêtes de commande (CQRS) dans le backend. -
Cohérence des données : les microservices sont propriétaires de leur magasin de données, et l'application monolithique peut également potentiellement utiliser ces données. Pour activer le partage, vous pouvez synchroniser le magasin de données des nouveaux microservices avec la base de données de l'application monolithique à l'aide d'une file d'attente et d'un agent. Cela peut toutefois entraîner une redondance des données et une éventuelle cohérence entre deux magasins de données. Nous vous recommandons donc de le traiter comme une solution tactique jusqu'à ce que vous puissiez établir une solution à long terme, telle qu'un lac de données.
Mise en œuvre
Dans le modèle Strangler Fig, vous remplacez une fonctionnalité spécifique par un nouveau service ou une nouvelle application, un composant à la fois. Une couche proxy intercepte les demandes envoyées à l'application monolithique et les achemine vers le système existant ou le nouveau système. Comme la couche proxy dirige les utilisateurs vers l'application appropriée, vous pouvez ajouter des fonctionnalités au nouveau système tout en veillant à ce que le monolithe continue de fonctionner. Le nouveau système remplace finalement toutes les fonctionnalités de l'ancien système, et vous pouvez le mettre hors service.
Architecture de haut niveau
Dans le schéma suivant, une application monolithique comporte trois services : le service utilisateur, le service de panier et le service de compte. Le service de panier dépend du service utilisateur et l'application utilise une base de données relationnelle monolithique.

La première étape consiste à ajouter une couche proxy entre l'interface utilisateur de Storefront et l'application monolithique. Au début, le proxy achemine tout le trafic vers l'application monolithique.

Lorsque vous souhaitez ajouter de nouvelles fonctionnalités à votre application, vous les implémentez sous forme de nouveaux microservices au lieu d'ajouter des fonctionnalités au monolithe existant. Cependant, vous continuez à corriger les bogues du monolithe pour garantir la stabilité de l'application. Dans le schéma suivant, la couche proxy achemine les appels vers le monolithe ou vers le nouveau microservice en fonction de l'URL de l'API.

Ajouter une couche anticorruption
Dans l'architecture suivante, le service utilisateur a été migré vers un microservice. Le service de panier appelle le service utilisateur, mais l'implémentation n'est plus disponible dans le monolithe. En outre, l'interface du service nouvellement migré peut ne pas correspondre à son interface précédente dans l'application monolithique. Pour faire face à ces changements, vous devez implémenter une ACL. Au cours du processus de migration, lorsque les fonctionnalités du monolithe doivent appeler les fonctionnalités qui ont été migrées en tant que microservices, l'ACL convertit les appels vers la nouvelle interface et les achemine vers le microservice approprié.

Vous pouvez implémenter l'ACL dans l'application monolithique sous la forme d'une classe spécifique au service qui a été migré ; par exemple, UserServiceFacade
ou. UserServiceAdapter
L'ACL doit être mise hors service une fois que tous les services dépendants ont été migrés vers l'architecture des microservices.
Lorsque vous utilisez l'ACL, le service cart appelle toujours le service utilisateur au sein du monolithe, et le service utilisateur redirige l'appel vers le microservice via l'ACL. Le service de panier doit toujours appeler le service utilisateur sans être au courant de la migration du microservice. Ce couplage souple est nécessaire pour réduire la régression et les perturbations des activités.
Gestion de la synchronisation des données
La meilleure pratique consiste à ce que le microservice soit propriétaire de ses données. Le service utilisateur stocke ses données dans son propre magasin de données. Il peut être nécessaire de synchroniser les données avec la base de données monolithique pour gérer les dépendances telles que les rapports et pour prendre en charge les applications en aval qui ne sont pas encore prêtes à accéder directement aux microservices. L'application monolithique peut également avoir besoin des données pour d'autres fonctions et composants qui n'ont pas encore été migrés vers des microservices. La synchronisation des données est donc nécessaire entre le nouveau microservice et le monolithe. Pour synchroniser les données, vous pouvez introduire un agent de synchronisation entre le microservice utilisateur et la base de données monolithique, comme indiqué dans le schéma suivant. Le microservice utilisateur envoie un événement à la file d'attente chaque fois que sa base de données est mise à jour. L'agent de synchronisation écoute la file d'attente et met continuellement à jour la base de données monolithique. Les données de la base de données monolithique sont finalement cohérentes avec les données en cours de synchronisation.

Migration de services supplémentaires
Lorsque le service de panier est migré hors de l'application monolithique, son code est révisé pour appeler directement le nouveau service, de sorte que l'ACL n'achemine plus ces appels. Le schéma suivant illustre cette architecture.

Le schéma suivant montre l'état d'étranglement final où tous les services ont été migrés hors du monolithe et où il ne reste que le squelette du monolithe. Les données historiques peuvent être migrées vers des magasins de données appartenant à des services individuels. L'ACL peut être retiré et le monolithe est prêt à être mis hors service à ce stade.

Le schéma suivant montre l'architecture finale après la mise hors service de l'application monolithique. Vous pouvez héberger les microservices individuels via une URL basée sur les ressources (telle quehttp://www.storefront.com/user
) ou via leur propre domaine (par exemple,http://user.storefront.com
) en fonction des exigences de votre application. Pour plus d'informations sur les principales méthodes permettant d'exposer le protocole HTTP aux consommateurs en amont APIs à l'aide de noms d'hôtes et de chemins, consultez la section sur les modèles de routage des API.

Mise en œuvre à l’aide des services AWS
Utilisation d'API Gateway comme proxy d'application
Le schéma suivant montre l'état initial de l'application monolithique. Supposons qu'il ait été AWS migré à l'aide d'une lift-and-shift stratégie. Il s'exécute donc sur une instance Amazon Elastic Compute Cloud (Amazon EC2)

Dans l'architecture suivante, AWS Migration Hub Refactor Spacesdéploie Amazon API Gateway

Le service utilisateur est migré vers une fonction Lambda, et une base de données Amazon DynamoDB stocke ses données

Dans le schéma suivant, le service cart a également été migré du monolithe vers une fonction Lambda. Une route et un point de terminaison de service supplémentaires sont ajoutés à Refactor Spaces, et le trafic est automatiquement transféré à la fonction Cart
Lambda. Le magasin de données pour la fonction Lambda est géré par Amazon. ElastiCache

Dans le schéma suivant, le dernier service (compte) est migré du monolithe vers une fonction Lambda. Il continue d'utiliser la base de données Amazon RDS d'origine. La nouvelle architecture comporte désormais trois microservices dotés de bases de données distinctes. Chaque service utilise un type de base de données différent. Ce concept d'utilisation de bases de données spécialement conçues pour répondre aux besoins spécifiques des microservices est appelé persistance polyglotte. Les fonctions Lambda peuvent également être implémentées dans différents langages de programmation, selon le cas d'utilisation. Lors du refactoring, Refactor Spaces automatise le transfert et le routage du trafic vers Lambda. Cela permet à vos concepteurs de gagner du temps pour concevoir, déployer et configurer l'infrastructure de routage.

Utilisation de plusieurs comptes
Dans l'implémentation précédente, nous utilisions un seul VPC doté d'un sous-réseau privé et d'un sous-réseau public pour l'application monolithique, et nous avons déployé les microservices au sein de celui-ci dans un souci de simplicité Compte AWS . Cependant, c'est rarement le cas dans les scénarios réels, où les microservices sont souvent déployés en plusieurs Comptes AWS pour garantir l'indépendance du déploiement. Dans une structure multi-comptes, vous devez configurer le routage du trafic du monolithe vers les nouveaux services dans différents comptes.
Refactor Spaces vous aide à créer et à configurer l' AWS infrastructure pour le routage des appels d'API en dehors de l'application monolithique. Refactor Spaces orchestre les politiques API Gateway
Supposons que les services utilisateur et panier soient déployés sur deux comptes différents, comme le montre le schéma suivant. Lorsque vous utilisez Refactor Spaces, il vous suffit de configurer le point de terminaison du service et l'itinéraire. Refactor Spaces automatise l'intégration entre API Gateway et Lambda et la création de politiques de ressources Lambda, afin que vous puissiez vous concentrer sur la refactorisation sécurisée des services hors du monolithe.

Pour un didacticiel vidéo sur l'utilisation des espaces de refactorisation, voir Refactoriser les applications de manière incrémentielle