Microservices : les bonnes pratiques de la gestion des données

Alexandre Brun
7 min readMar 11, 2021

--

Cet article est le cinquième d’une série rédigée par Thomas Ruiz et Alexandre Brun traitant de l’architecture orientée microservices (retrouvez l’article introductif ici). Nous tenons à remercier particulièrement Antoine Haguenauer et Mohamed Amine Bergaoui pour leurs précieux conseils.

On ne peut pas parler de microservices sans aborder la question des données. Qu’elles soient sensibles ou publiques, en petite ou en grosse quantité, éphémères ou persistantes, les données sont la matière première des applications et plus largement, des systèmes d’information.

Quelles sont les spécificités de la gestion des données dans une architecture microservices ? Comment mettre en place une architecture microservices efficace dans la gestion et le traitement des données ?

Nous exposerons les principes fondamentaux de la gestion des données dans le cadre d’un projet orienté microservices. Nous détaillerons ensuite les bonnes pratiques et les retours d’expérience de Neoxia sur la mise en place de telles architectures.

Disclaimer:

Les principes que nous allons détailler s’appliquent uniquement aux bases de données orientées traitement transactionnel. Les bases de données qui sont utilisées pour le Big Data (Redshift, BigQuery, Azure Synapse, Teradata, …) sont exclues de ce périmètre car les appels API auraient des conséquences négatives sur les performances. Elles sont faites pour un usage principalement orienté traitement analytique en ligne {OLAP}.

Deux principes fondamentaux de gestion des données

Les bases de données d’une application orientée microservices doivent respecter deux principes : la séparation des préoccupations et la responsabilité des données.

1️⃣ Le principe de la séparation des préoccupations (Separation Of Concerns) est un concept issu de l’informatique théorique qui vise à segmenter un programme informatique en parties isolées dont chacune gère un aspect précis du problème. Comme nous l’avons vu dans l’article Comment bien organiser un projet microservices, on privilégie la séparation des macroprocessus à l’intérieur de contextes bornés pour la mise en place d’une architecture orientée microservices.

2️⃣ Le second principe est que chaque microservice est responsable de ses données, de leur cohérence et de leur intégrité. Chaque service doit ainsi permettre un accès restreint à ses données par le biais d’une API. Son fonctionnement interne est cloisonné pour n’exposer que ce qu’il souhaite, et empêcher toute modification externe non désirée. Une même base de données ne doit en aucun cas être gérée par deux microservices différents. Cela permet d’éviter les couplages non intentionnels et assure la cohérence et l’intégrité des données, tout en gardant la possibilité de changer les schémas de relations sans impacter d’autres services.

Fig 1: illustration du principe de responsabilité des données et de séparation des préoccupations : à chaque service sa table

La mise en oeuvre des principes

Ces deux principes acquis, comment les appliquer ? Nous traitons deux grands enjeux : la structuration des échanges et la gestion des fichiers dans le Cloud.

La structuration des échanges

Les échanges s’organisent autour de trois séries d’instruments, à mobiliser de façon progressive en fonction du niveau de maturité de l’équipe tech : les transactions multiservices, l’event sourcing, le pattern CQRS.

1. Niveau débutant: les transactions multi-services

Prenons le cas très simple d’un achat sur un site e-commerce : imaginons qu’un microservice gère le stock des produits disponibles et qu’un autre soit chargé des commandes. Lorsqu’une commande est effectuée, le microservice de gestion du stock est appelé pour être mis à jour. Il répond à son tour au microservice de commande pour confirmer la disponibilité du produit. En cas de dysfonctionnement de l’un ou de l’autre, il faut pouvoir interrompre la commande et garder une information fiable sur l’état du stock.

Nous identifions 4 pratiques standard à implémenter pour garantir la cohérence des échanges multi-services :

  1. Le court-circuitage : le pattern de circuit-breaker (popularisé par Michael Nygard dans Release It), permet d’éviter les problèmes en cascade lorsqu’un service à distance se bloque ou n’adresse pas de réponse après un certain délai. Il consiste à encapsuler un appel de fonction dans votre objet pour surveiller les pannes. Une fois que celles-ci ont atteint un certain seuil, tous les appels retournent une erreur, sans que l’appel au service ne soit effectué.
  2. Le traitement asynchrone, qui permet de ne pas bloquer les requêtes. Chez Neoxia, nous considérons cette pratique comme indispensable pour tout bon service délivrant de l’information à grande échelle.
  3. La gestion des erreurs, bonne pratique standard chez Neoxia. Pour prendre en compte les défaillances d’un système ou d’un service, il faut très bien connaître le métier pour lequel le service est développé. Les différents cas d’usages et d’erreurs doivent être identifiés et testés. Pour ce faire, on peut opter pour le TDD (Test-Driven Development) ou la revue les cas d’usages avec un spécialiste du métier. Chez Neoxia, nous recommandons cette seconde approche qui réduit l’ambiguïté entre la compréhension du métier de l’équipe tech et celle du spécialiste.
  4. La mise en cache : la quantité et la taille des échanges de données entre microservices via appels API peut finir par coûter très cher. Nous recommandons la mise en place d’un ou plusieurs systèmes de mise en cache (in-memory, key-value store, etc).

2. Niveau avancé : l’event sourcing

L’event sourcing est une autre façon plus récente de respecter les principes de séparation des préoccupations et le périmètre de responsabilité. L’event sourcing consiste à stocker tout ce qu’il s’est passé dans un système sous forme d’événements dans le but de conserver tout changement du modèle.

Dans l’exemple de notre site e-commerce, on créerait des évènements tels que “Panier Créé”, “Panier Modifié”, “Commande Effectuée”. Ces événements persisteraient dans l’Event Store, sorte de gestionnaire de versions des événements. On pourrait alors facilement revenir à un état des données antérieur.

3. Niveau expert : le pattern CQRS

Pour les plus expérimentés, le pattern CQRS (Command and Query Responsibility Segregation) permet de séparer la partie lecture de données (Query) de la partie écriture (Command) par une révolution de l’approche de la gestion des données dans l’architecture logicielle.

Classiquement, l’approche CRUD utilise la même base de données pour la lecture et l’écriture de données. Elle se heurte à une réalité de terrain : que se passe-t-il lorsqu’un utilisateur A modifie la donnée en base, tandis qu’en même temps un utilisateur B consulte cette donnée ? Quelles conséquences sur les performances de l’application?

Le pattern CQRS permet de répondre à ces contraintes en abandonnant l’architecture 3 Tiers (IHM, Controllers, DB) et en séparant les composants qui requêtent les données de ceux qui les modifient. Tandis que la partie lecture ne fait que retourner une valeur à l’utilisateur, la partie écriture enregistre l’input de l’utilisateur dans une autre base de données. Elle publie la commande sous forme d’un événement qui sera envoyé à la base qui gère les queries pour signaler la modification.

Au-delà de la structuration des échanges entre les microservices, un second enjeu-clé de la mise en œuvre des principes concerne le cas spécifique de la gestion des fichiers.

La gestion des fichiers dans le Cloud

Ce qu’on désigne par données est vaste et ne se limite pas à des abstractions binaires dans des bases. Pour l’utilisateur, les données correspondent à des éléments tangibles : des photos, des documents, des traces de passage révélatrices de leur identité. Dans une approche orientée microservices, la gestion des fichiers est une préoccupation centrale. Chez Neoxia, nous avons identifié les bonnes pratiques suivantes :

1. Stockage de fichiers dans le Cloud dans des buckets sécurisés

De nombreux cas d’usages peuvent nous amener à stocker des fichiers, parfois sensibles. Par exemple, le téléversement de pièces justificatives privées (photos, pièces d’identité, factures, etc.).

Dans le cadre d’une architecture orientée microservices, la gestion de cette tâche est assurée par un service dédié. De cette manière, les autres microservices de l’application ne seront pas sollicités pour gérer les documents, allégeant ainsi la base de code et respectant le principe de séparation des préoccupations.

Chez Neoxia, nous utilisons de préférence le système de stockage S3 d’Amazon Web Services. Celui-ci permet d’utiliser des buckets privés accessibles de façon sécurisée grâce à la génération d’url signées à durée de vie courte. Par ailleurs, le service S3 dé-corrélé de la base de code permet de gérer la charge intelligemment, en fonction des volumes d‘appels et ainsi d’optimiser la facture.

2. Zero Trust

Une politique de gestion des identités qui inclut la gestion fine des droits des utilisateurs selon un modèle de Zero trust (confiance zéro). Dans le cas de la gestion des fichiers, cela consiste à appliquer des droits de lecture ou d’écriture sur les données en fonction des composants qui sont susceptibles d’interagir avec vos données mais aussi en fonction des types d’utilisateurs.

fig 2: Par une gestion fine des droits, chaque utilisateur n’accède qu’aux fichiers de son périmètre.

3. Chiffrement des données, en transit et au repos

Les données sont chiffrées, conformément au principe de Zero trust et au Règlement général de protection des données (RGPD). Le chiffrage est possible à la fois au niveau de la base de données et au niveau des fichiers. Il existe deux types de chiffrement, complémentaires :

  • le chiffrement en transit, lors du passage des données d’un système à un autre : points de terminaison en HTTPS et backends pourvus de certificats afin de garantir aux clients leur identité.
  • le chiffrement au repos, à l’intérieur même des bases de données.

Conclusion

On ne peut s’intéresser aux architectures microservices sans se plonger dans la façon dont la gestion des données est affectée. La séparation des préoccupations et la responsabilité de chaque service en sont deux piliers majeurs. Ils se traduisent par la mise en place des pratiques évoquées, mais ce ne sont pas les seules.

Quelles sont celles qui mériteraient d’être ajoutées ? Donnez-nous votre avis et discutons !

Cet article clôt notre série autour des microservices et de l’approche DDD. Nous allons compiler et augmenter ces articles avec vos contributions et le mettre à disposition sous forme de livre blanc. Nous vous tiendrons informés sur nos réseaux de la date de publication. D’ici là, n’hésitez pas à venir échanger avec nous !

--

--