BLOG | NGINX

Refactorisation d'un monolithe en microservices

NGINX-Partie-de-F5-horiz-black-type-RGB
Miniature de Chris Richardson
Chris Richardson
Publié le 08 mars 2016

Rédacteur – Cette série d’articles en sept parties est désormais terminée :

  1. Introduction aux microservices
  2. Création de microservices : Utilisation d'une passerelle API
  3. Création de microservices : Communication interprocessus dans une architecture de microservices
  4. Découverte de services dans une architecture de microservices
  5. Gestion des données pilotée par les événements pour les microservices
  6. Choisir une stratégie de déploiement de microservices
  7. Refactorisation d'un monolithe en microservices (cet article)

Vous pouvez également télécharger l'ensemble complet des articles, ainsi que des informations sur la mise en œuvre de microservices à l'aide de NGINX Plus, sous forme d'ebook – Microservices : De la conception au déploiement . Et consultez notre série sur l’ architecture de référence des microservices et la page Solutions de microservices .

Il s’agit du septième et dernier article de ma série sur la création d’applications avec des microservices. Le premier article présente le modèle d’architecture des microservices et discute des avantages et des inconvénients de l’utilisation des microservices. Les articles suivants abordent différents aspects de l’architecture des microservices : l’utilisation d’une passerelle API<.htmla>, la communication interprocessus , la découverte de services , la gestion des données pilotée par les événements et le déploiement de microservices . Dans cet article, nous examinons les stratégies de migration d’une application monolithique vers des microservices.

J’espère que cette série d’articles vous a donné une bonne compréhension de l’architecture des microservices, de ses avantages et de ses inconvénients, et quand l’utiliser. Peut-être que l’architecture de microservices convient parfaitement à votre organisation.

Cependant, il y a de fortes chances que vous travailliez sur une application monolithique volumineuse et complexe. Votre expérience quotidienne de développement et de déploiement de votre application est lente et pénible. Les microservices semblent être un nirvana lointain. Heureusement, il existe des stratégies que vous pouvez utiliser pour échapper à l’enfer monolithique. Dans cet article, je décris comment refactoriser progressivement une application monolithique en un ensemble de microservices.

Présentation de la refactorisation vers les microservices

Le processus de transformation d’une application monolithique en microservices est une forme de modernisation des applications . C’est quelque chose que les développeurs font depuis des décennies. Par conséquent, il existe certaines idées que nous pouvons réutiliser lors de la refactorisation d’une application en microservices.

Une stratégie à ne pas utiliser est la réécriture du « Big Bang ». C’est à ce moment-là que vous concentrez tous vos efforts de développement sur la création d’une nouvelle application basée sur des microservices à partir de zéro. Même si cela semble attrayant, c’est extrêmement risqué et cela se terminera probablement par un échec. Comme l’aurait dit Martin Fowler, « la seule chose qu’une réécriture du Big Bang garantit, c’est un Big Bang ! »

Au lieu d’une réécriture Big Bang, vous devriez refactoriser progressivement votre application monolithique. Vous créez progressivement une nouvelle application composée de microservices et l’exécutez conjointement avec votre application monolithique. Au fil du temps, la quantité de fonctionnalités implémentées par l’application monolithique diminue jusqu’à ce qu’elle disparaisse complètement ou qu’elle devienne simplement un autre microservice. Cette stratégie s’apparente à l’entretien de votre voiture tout en roulant sur l’autoroute à 70 mph – un défi, mais bien moins risqué que de tenter une réécriture Big Bang.

Martin Fowler fait référence à cette stratégie de modernisation des applications sous le nom d'application Strangler . Le nom vient de la vigne étrangleuse (également appelée figue étrangleuse) que l'on trouve dans les forêts tropicales. Une vigne étrangleuse pousse autour d'un arbre afin d'atteindre la lumière du soleil au-dessus de la canopée de la forêt. Parfois, l'arbre meurt, laissant derrière lui une vigne en forme d'arbre. La modernisation des applications suit le même modèle. Nous allons construire une nouvelle application composée de microservices autour de l'application héritée, qui finira par mourir.

Voyons différentes stratégies pour y parvenir.

Stratégie 1 – Arrêtez de creuser

La loi des trous dit que chaque fois que vous êtes dans un trou, vous devez arrêter de creuser. C'est un excellent conseil à suivre lorsque votre application monolithique est devenue ingérable. En d’autres termes, vous devriez arrêter d’agrandir le monolithe. Cela signifie que lorsque vous implémentez une nouvelle fonctionnalité, vous ne devez pas ajouter de code supplémentaire au monolithe. Au lieu de cela, la grande idée de cette stratégie est de placer ce nouveau code dans un microservice autonome. Le diagramme suivant montre l’architecture du système après l’application de cette approche.

Outre le nouveau service et l’ancien monolithe, il existe deux autres composants. Le premier est un routeur de requêtes, qui gère les requêtes entrantes (HTTP). Elle est similaire à la passerelle API décrite dans un article précédent<.htmla>. Le routeur envoie des requêtes correspondant aux nouvelles fonctionnalités au nouveau service. Il achemine les requêtes héritées vers le monolithe.

L’autre composant est le code de colle, qui intègre le service au monolithe. Un service existe rarement de manière isolée et doit souvent accéder aux données appartenant au monolithe. Le code de colle, qui réside dans le monolithe, le service ou les deux, est responsable de l'intégration des données. Le service utilise le code glue pour lire et écrire les données appartenant au monolithe.

Il existe trois stratégies qu'un service peut utiliser pour accéder aux données du monolithe :

  • Invoquer une API distante fournie par le monolithe
  • Accéder directement à la base de données du monolithe
  • Conserver sa propre copie des données, qui est synchronisée avec la base de données du monolithe

Le code de colle est parfois appelé couche anti-corruption . C'est parce que le code de colle empêche le service, qui possède son propre modèle de domaine vierge, d'être pollué par des concepts provenant du modèle de domaine du monolithe hérité. Le code de colle traduit entre les deux modèles différents. Le terme « couche anti-corruption » est apparu pour la première fois dans l’ouvrage incontournable Domain Driven Design d’Eric Evans et a ensuite été affiné dans un livre blanc . Développer une couche anti-corruption peut être une tâche non négligeable. Mais il est essentiel d’en créer un si vous voulez sortir de l’enfer monolithique.

La mise en œuvre de nouvelles fonctionnalités en tant que service léger présente plusieurs avantages. Cela empêche le monolithe de devenir encore plus ingérable. Le service peut être développé, déployé et mis à l’échelle indépendamment du monolithe. Vous bénéficiez des avantages de l’architecture de microservices pour chaque nouveau service que vous créez.

Cependant, cette approche ne résout en rien les problèmes liés au monolithe. Pour résoudre ces problèmes, vous devez briser le monolithe. Voyons des stratégies pour y parvenir.

Stratégie 2 – Séparer le frontend et le backend

Une stratégie permettant de réduire la taille de l’application monolithique consiste à séparer la couche de présentation des couches de logique métier et d’accès aux données. Une application d’entreprise typique se compose d’au moins trois types de composants différents :

  • Couche de présentation – Composants qui gèrent les requêtes HTTP et implémentent soit une API (REST), soit une interface utilisateur Web basée sur HTML. Dans une application dotée d'une interface utilisateur sophistiquée, la couche de présentation est souvent un corpus de code conséquent.
  • Couche logique métier – Composants qui constituent le cœur de l’application et implémentent les règles métier.
  • Couche d’accès aux données – Composants qui accèdent aux composants d’infrastructure tels que les bases de données et les courtiers de messages.

Il existe généralement une séparation nette entre la logique de présentation d’un côté et la logique métier et d’accès aux données de l’autre. Le niveau métier dispose d’une API à granularité grossière composée d’une ou de plusieurs façades, qui encapsulent des composants de logique métier. Cette API est une couture naturelle le long de laquelle vous pouvez diviser le monolithe en deux applications plus petites. Une application contient la couche de présentation. L’autre application contient la logique métier et d’accès aux données. Après la division, l’application de logique de présentation effectue des appels distants à l’application de logique métier. Le diagramme suivant montre l'architecture avant et après le refactoring.

Diviser un monolithe de cette manière présente deux avantages principaux. Il vous permet de développer, de déployer et de faire évoluer les deux applications indépendamment l'une de l'autre. En particulier, il permet aux développeurs de la couche de présentation d’itérer rapidement sur l’interface utilisateur et d’effectuer facilement des tests A/B, par exemple. Un autre avantage de cette approche est qu’elle expose une API distante qui peut être appelée par les microservices que vous développez.

Cette stratégie n’est cependant qu’une solution partielle. Il est très probable que l’une ou les deux applications soient un monolithe ingérable. Vous devez utiliser la troisième stratégie pour éliminer le ou les monolithes restants.

Stratégie 3 – Extraire les services

La troisième stratégie de refactorisation consiste à transformer les modules existants au sein du monolithe en microservices autonomes. Chaque fois que vous extrayez un module et le transformez en service, le monolithe rétrécit. Une fois que vous aurez converti suffisamment de modules, le monolithe ne sera plus un problème. Soit il disparaît complètement, soit il devient suffisamment petit pour n’être qu’un autre service.

Prioriser les modules à convertir en services

Une application monolithique volumineuse et complexe se compose de dizaines ou de centaines de modules, tous candidats à l’extraction. Déterminer quels modules convertir en premier est souvent un défi. Une bonne approche consiste à commencer avec quelques modules faciles à extraire. Cela vous donnera de l'expérience avec les microservices en général et le processus d'extraction en particulier. Après cela, vous devez extraire les modules qui vous apporteront le plus d’avantages.

La conversion d’un module en service prend généralement du temps. Vous souhaitez classer les modules en fonction des avantages que vous en tirerez. Il est généralement avantageux d’extraire les modules qui changent fréquemment. Une fois que vous avez converti un module en service, vous pouvez le développer et le déployer indépendamment du monolithe, ce qui accélérera le développement.

Il est également avantageux d’extraire des modules dont les besoins en ressources sont sensiblement différents de ceux du reste du monolithe. Il est utile, par exemple, de transformer un module doté d’une base de données en mémoire en un service, qui peut ensuite être déployé sur des hôtes disposant de grandes quantités de mémoire. De même, il peut être intéressant d’extraire des modules qui implémentent des algorithmes coûteux en calcul, car le service peut ensuite être déployé sur des hôtes dotés de nombreux processeurs. En transformant des modules ayant des besoins en ressources particuliers en services, vous pouvez rendre votre application beaucoup plus facile à faire évoluer.

Pour déterminer les modules à extraire, il est utile de rechercher les limites grossières existantes (également appelées coutures). Ils permettent de transformer plus facilement et à moindre coût des modules en services. Un exemple d’une telle limite est un module qui communique uniquement avec le reste de l’application via des messages asynchrones. Il peut être relativement peu coûteux et facile de transformer ce module en microservice.

Comment extraire un module

La première étape de l’extraction d’un module consiste à définir une interface à gros grains entre le module et le monolithe. Il s’agit probablement d’une API bidirectionnelle, puisque le monolithe aura besoin de données appartenant au service et vice versa. Il est souvent difficile d’implémenter une telle API en raison des dépendances enchevêtrées et des modèles d’interaction précis entre le module et le reste de l’application. La logique métier implémentée à l’aide du modèle de domaine est particulièrement difficile à refactoriser en raison des nombreuses associations entre les classes de modèles de domaine. Vous devrez souvent apporter des modifications de code importantes pour briser ces dépendances. Le diagramme suivant montre le refactoring.

Une fois que vous avez implémenté l’interface à granularité grossière, vous transformez le module en un service autonome. Pour ce faire, vous devez écrire du code pour permettre au monolithe et au service de communiquer via une API qui utilise un mécanisme de communication interprocessus (IPC). Le diagramme suivant montre l’architecture avant, pendant et après la refactorisation.

Dans cet exemple, le module Z est le module candidat à extraire. Ses composants sont utilisés par le module X et il utilise le module Y. La première étape de refactorisation consiste à définir une paire d'API à gros grains. La première interface est une interface entrante utilisée par le module X pour appeler le module Z. La seconde est une interface sortante utilisée par le module Z pour appeler le module Y.

La deuxième étape de refactorisation transforme le module en un service autonome. Les interfaces entrantes et sortantes sont implémentées par du code qui utilise un mécanisme IPC. Vous devrez probablement créer le service en combinant le module Z avec un framework Microservice Chassis qui gère les problèmes transversaux tels que la découverte de services.

Une fois que vous avez extrait un module, vous disposez d’un autre service qui peut être développé, déployé et mis à l’échelle indépendamment du monolithe et de tout autre service. Vous pouvez même réécrire le service à partir de zéro ; dans ce cas, le code API qui intègre le service au monolithe devient une couche anti-corruption qui assure la traduction entre les deux modèles de domaine. Chaque fois que vous extrayez un service, vous faites un pas de plus vers les microservices. Au fil du temps, le monolithe rétrécira et vous aurez un nombre croissant de microservices.

Résumé

Le processus de migration d’une application existante vers des microservices est une forme de modernisation des applications. Vous ne devez pas passer aux microservices en réécrivant votre application à partir de zéro. Au lieu de cela, vous devez refactoriser progressivement votre application en un ensemble de microservices. Vous pouvez utiliser trois stratégies : implémenter de nouvelles fonctionnalités sous forme de microservices ; séparer les composants de présentation des composants métier et d’accès aux données ; et convertir les modules existants du monolithe en services. Au fil du temps, le nombre de microservices augmentera, ainsi que l’agilité et la vélocité de votre équipe de développement.

Rédacteur – Cette série d’articles en sept parties est désormais terminée :

  1. Introduction aux microservices
  2. Création de microservices : Utilisation d'une passerelle API
  3. Création de microservices : Communication interprocessus dans une architecture de microservices
  4. Découverte de services dans une architecture de microservices
  5. Gestion des données pilotée par les événements pour les microservices
  6. Choisir une stratégie de déploiement de microservices
  7. Refactorisation d'un monolithe en microservices (cet article)

Vous pouvez également télécharger l'ensemble complet des articles, ainsi que des informations sur la mise en œuvre de microservices à l'aide de NGINX Plus, sous forme d'ebook – Microservices : De la conception au déploiement . Et consultez notre série sur l’ architecture de référence des microservices et la page Solutions de microservices .

Le blogueur invité Chris Richardson est le fondateur du CloudFoundry.com original, un PaaS (Platform as a Service) Java précoce pour Amazon EC2. Il conseille désormais les organisations pour améliorer la manière dont elles développent et déploient des applications. Il blogue également régulièrement sur les microservices sur https://microservices.io


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."