DBAtlas · Atlas d'exploitation des bases de données

Architecture de référence : couche de données microservices

Dans les microservices, la décision la plus critique est la propriété des données : chaque service doit posséder ses données (database-per-service) et ne doit pas accéder directement aux tables d'un autre service. Cet arbre parcourt pas à pas l'anti-pattern de la base partagée, l'obtention de la cohérence inter-services sans transactions distribuées (saga + outbox), la synchronisation des données par CDC et les requêtes inter-services (API composition vs modèle de lecture CQRS). L'objectif est de garder des services réellement indépendants et déployables séparément.

Comment les services possèdent-ils les données : une base partagée ou une par service ?

L'isolation des données est la colonne vertébrale d'une architecture microservices. Les services utilisent-ils une seule base partagée (une dépendance cachée), chaque service a-t-il sa propre base (le bon schéma), ou le vrai défi est-il la cohérence/les requêtes inter-services ? Établissez d'abord l'état actuel.

  • Tous les services utilisent une seule base partagée
  • Chaque service a sa propre base, mais les opérations exigent une cohérence inter-services
  • Chaque service a sa propre base, mais des requêtes/rapports combinés sont nécessaires

Comment garderez-vous cohérent un workflow inter-services ?

Pour une opération couvrant plusieurs services (créer la commande → encaisser le paiement → décrémenter le stock), une transaction distribuée classique (2PC) est fragile et ne passe pas à l'échelle. À la place, découpez le flux en étapes transaction locale + événement (saga) et publiez les événements de façon fiable (outbox). Quel problème rencontrez-vous ?

  • J'essaie de rendre atomique un workflow multi-services (2PC ?)
  • Je n'arrive pas à rendre cohérentes l'écriture locale et la publication d'événement (double écriture)

Comment servirez-vous une requête qui affiche des données provenant de plusieurs services ?

Une fois les données réparties entre services, vous ne pouvez plus construire un écran combiné avec une seule jointure. Il existe deux approches principales : appeler les API des services concernés au moment de la requête et fusionner les résultats (API composition), ou maintenir un modèle de lecture pré-joint alimenté par des événements (CQRS). La fréquence et la complexité des requêtes déterminent le choix.

  • Jointure simple et peu fréquente ; appeler quelques services et fusionner suffit
  • Requête combinée complexe/fréquente ; un modèle de lecture prêt à l'emploi est nécessaire

Recommandation : database-per-service (découper la base partagée)

Des services partageant une même base constituent une dépendance cachée et dangereuse : la modification de schéma d'un service casse les autres, la propriété des tables devient floue et le déploiement indépendant devient impossible. Chaque service doit posséder ses données.

Recommandation : schéma saga (éviter les transactions distribuées)

N'essayez pas de rendre atomique une opération couvrant plusieurs services avec une seule transaction distribuée (2PC) ; le 2PC retient des verrous et apporte une fragilité de point unique et une faible disponibilité. À la place, découpez le flux en étapes constituant chacune une transaction locale dans son propre service, et annulez-les par des étapes de compensation en cas d'échec.

Recommandation : transactional outbox (le piège de la double écriture)

Lorsqu'un service veut à la fois écrire dans sa propre base et publier un événement, faire les deux séparément est le piège de la « double écriture » : si l'une réussit et l'autre échoue, le système reste incohérent. La solution consiste à écrire l'événement dans une table outbox dans la MÊME transaction locale que les données métier, puis à le publier via un processus distinct.

Recommandation : API composition (fusion au moment de la requête)

Lorsque des données combinées sont nécessaires, l'appelant (par ex. une gateway/BFF) appelle les API des services concernés et fusionne les résultats en mémoire. C'est la solution la plus simple pour des jointures simples couvrant peu de services ; aucune copie de données supplémentaire n'est conservée.

Recommandation : modèle de lecture CQRS (alimenté par des événements)

Pour des requêtes combinées complexes et fréquentes, conservez un modèle de lecture pré-joint et optimisé pour les requêtes (vue matérialisée/copie dénormalisée) alimenté par les événements émis par les services. Le côté écriture reste dans les services ; le côté lecture sert rapidement des données prêtes depuis un seul endroit.

Ouvrir dans DBAtlas →