Mon équipe migre d'une application ASP.NET monolithique vers .NET Core et Kubernetes. Les changements de code semblent aller aussi bien que prévu, mais là où mon équipe rencontre beaucoup de discorde, c'est autour de la base de données.
Nous avons actuellement une base de données SQL Server assez volumineuse qui héberge toutes les données pour l'ensemble de notre entreprise. Je propose que nous divisions la base de données d'une manière similaire à la division du code - les données de catalogue dans une base de données (logique), les données d'inventaire dans une autre, les commandes dans une autre, etc. - et chaque microservice serait le gardien de sa base de données .
L'implication ici est que les clés étrangères qui traversent les limites des microservices devraient être supprimées et les sprocs et les vues qui traversent les frontières seraient interdits. Tous les modèles de données peuvent ou non résider dans la même base de données physique, mais même s'ils le font, ils ne doivent pas interagir directement les uns avec les autres. Les commandes peuvent toujours référencer les éléments de catalogue par ID, mais l'intégrité des données ne sera pas strictement appliquée au niveau de la base de données et ces données devront être jointes en code plutôt qu'en SQL.
Je vois la perte de ces derniers comme des compromis nécessaires pour passer au microservice et obtenir les avantages d'évolutivité qui vont avec. Tant que nous choisissons judicieusement nos coutures et nous développons autour d'elles, cela devrait être OK. D'autres membres de l'équipe sont convaincus que tout doit rester dans la même base de données monolithique afin que tout puisse être ACID et que l'intégrité référentielle soit préservée partout.
Cela m'amène à ma question. Premièrement, ma position sur les contraintes des clés étrangères et l'adhésion est-elle plausible? Dans l'affirmative, quelqu'un est-il au courant de la lecture crédible que je pourrais offrir à mes collègues? Leur position est presque religieuse et ils ne semblent pas être influencés par quoi que ce soit à moins que Martin Fowler lui-même leur dise qu'ils ont tort.
Il n'y a pas de solution claire car cela dépend entièrement de votre contexte - en particulier, selon les dimensions que votre système est censé mettre à l'échelle et quels sont vos problèmes réels. La base de données est-elle vraiment votre goulot d'étranglement?
Cette réponse (malheureusement assez longue) se lira un peu comme "les microservices sont mauvais, des monolithes à vie!", Mais ce n'est pas mon intention. Mon point est que les microservices et les bases de données distribuées peuvent résoudre divers problèmes, mais non sans avoir certains problèmes qui leur sont propres. Afin de présenter un argument solide pour votre architecture, vous devez montrer que ces problèmes ne s'appliquent pas, peuvent être atténués et que cette architecture est le meilleur choix pour les besoins de votre entreprise.
La même flexibilité qui permet une meilleure mise à l'échelle est le revers des garanties plus faibles. Notamment, les systèmes distribués sont beaucoup beaucoup plus difficiles à raisonner.
Les mises à jour atomiques, les transactions, la cohérence/l'intégrité référentielle et la durabilité sont extrêmement précieuses et ne doivent pas être abandonnées précipitamment. Il ne sert à rien d'avoir des données si elles sont incomplètes, obsolètes ou carrément erronées. Lorsque ACID est une exigence commerciale mais que vous utilisez une technologie de base de données qui ne peut pas l'offrir immédiatement (par exemple, de nombreuses bases de données NoSQL ou une architecture DB-per-microservice), alors votre application doit combler le vide et fournir ces garanties.
Ce n'est pas impossible à faire, mais difficile à faire. Très délicat. Surtout dans un environnement distribué où il y a plusieurs écrivains dans chaque base de données. Cette difficulté se traduit par un risque élevé de bogues, pouvant inclure des données perdues, des données incohérentes, etc.
Par exemple, pensez à lire les analyses Jepsen de systèmes de bases de données distribuées bien connus , peut-être en commençant par les analyse de Cassandra . Je ne comprends pas la moitié de cette analyse, mais le TL; DR est que les systèmes distribués sont si difficiles que même les projets leaders de l'industrie se trompent parfois, d'une manière qui peut sembler évidente avec le recul.
Les systèmes distribués impliquent également un effort de développement plus important. Dans une certaine mesure, il existe un compromis direct entre les coûts de développement ou la perte d'argent sur du matériel plus robuste.
Dans la pratique, vous ne devriez pas regarder l'informatique mais les besoins de votre entreprise pour voir si et comment l'ACID peut être assoupli. Par exemple. de nombreuses relations avec des clés étrangères peuvent ne pas être aussi importantes qu'elles le semblent. Considérons une relation produit - catégorie n: m. Dans un SGBDR, nous pourrions utiliser une contrainte de clé étrangère afin que seuls les produits existants et les catégories existantes puissent faire partie de cette relation. Que se passe-t-il si nous introduisons des services de produits et de catégories distincts et qu'un produit ou une catégorie est supprimé?
Dans ce cas, cela pourrait ne pas être un gros problème et nous pouvons écrire notre application afin qu'elle filtre tous les produits ou catégories qui n'existent plus. Mais il y a des compromis!
Notez que cela peut nécessiter un niveau d'application JOIN
sur plusieurs bases de données/microservices, qui déplace simplement le traitement du serveur de base de données vers votre application. Cela augmente la charge totale et doit déplacer des données supplémentaires à travers le réseau.
Cela peut gâcher la pagination. Par exemple. vous demandez les 25 produits suivants dans une catégorie et filtrez les produits indisponibles de cette réponse. Votre application affiche désormais 23 produits. En théorie, une page avec zéro produit serait également possible!
Vous souhaiterez parfois exécuter un script qui nettoie les références pendantes, soit après chaque modification pertinente, soit à intervalles réguliers. Notez que ces scripts sont assez chers car ils doivent demander chaque produit/catégorie à la base de données/microservice de support pour voir s'il existe toujours.
Cela devrait être évident, mais pour plus de clarté: ne réutilisez pas les identifiants. Les ID de style à incrémentation automatique peuvent être corrects ou non. Les GUID ou les hachages vous offrent plus de flexibilité, par exemple en pouvant attribuer un ID avant que l'élément ne soit inséré dans une base de données.
Considérez maintenant plutôt une relation produit - commande. Qu'advient-il d'une commande si un produit est supprimé ou modifié? Ok, nous pouvons simplement copier les données produit pertinentes dans l'entrée de commande pour les garder disponibles - échange d'espace disque pour plus de simplicité. Mais que se passe-t-il si le prix du produit change ou si le produit devient indisponible juste avant la commande de ce produit? Dans un système distribué, les effets prennent du temps à se propager et la commande passera probablement par des données obsolètes.
Encore une fois, la façon d'aborder cela dépend des besoins de votre entreprise. Peut-être que la commande obsolète est acceptable, et vous pouvez ensuite annuler la commande si elle ne peut pas être exécutée.
Mais ce n'est peut-être pas une option, par exemple pour les paramètres hautement concurrents. Considérez 3000 personnes se précipitant pour acheter des billets de concert dans les 10 premières secondes, et supposons qu'un changement de disponibilité nécessitera 10 ms pour se propager. Quelle est la probabilité de vendre le dernier billet à plusieurs personnes? Dépend de la façon dont ces collisions sont gérées, mais en utilisant une distribution de Poisson avec λ = 3000 / (10s / 10ms) = 3
nous obtenons une P(k > 1) = 1 - P(k = 0) - P(k = 1) = 80%
chance de collision par intervalle de 10 ms. Que la vente et l'annulation ultérieure de la majorité de vos commandes soient possibles sans commettre de fraude pourrait conduire à une conversation intéressante avec votre service juridique.
La bonne nouvelle est que vous n'avez pas à passer à un modèle de base de données distribuée, si ce n'est pas requis autrement. Personne ne révoquera votre adhésion au Microservice Club si vous ne faites pas les microservices "correctement", car il n’existe pas un tel club - et il n’existe pas de véritable moyen de créer des microservices.
Le pragmatisme l'emporte à chaque fois, alors mélangez et associez différentes approches pour résoudre votre problème. Cela pourrait même signifier des microservices avec une base de données centralisée. Vraiment, ne passez pas par la douleur des bases de données distribuées si vous n'êtes pas obligé.
Les microservices présentent deux avantages majeurs:
Si une mise à l'échelle indépendante n'est pas requise, les microservices sont beaucoup moins attrayants.
Un serveur de base de données est déjà une sorte de service que vous pouvez faire évoluer (quelque peu) indépendamment, par ex. en ajoutant des répliques en lecture. Vous mentionnez des procédures stockées. Les réduire pourrait avoir un effet si important que toute autre discussion sur l'évolutivité serait sans objet.
Et il est parfaitement possible d'avoir un monolithe évolutif qui inclut tous les services sous forme de bibliothèques. Vous pouvez ensuite évoluer en lançant plus d'instances du monolithe, ce qui nécessite bien sûr que chaque instance soit sans état.
Cela a tendance à bien fonctionner jusqu'à ce que le monolithe soit trop grand pour être raisonnablement déployé, ou si certains services ont des besoins en ressources spéciaux pour que vous souhaitiez les faire évoluer indépendamment. Les domaines problématiques qui impliquent des ressources supplémentaires peuvent ne pas impliquer un modèle de données distinct.
Vous êtes conscient des besoins commerciaux de votre organisation et pouvez donc créer un argument pour une architecture de base de données par microservice, basée sur une analyse:
À l'inverse, si vous n'êtes pas en mesure de le démontrer, en particulier si la conception actuelle de la base de données est capable de prendre en charge une échelle suffisante à l'avenir (comme vos collègues semblent le croire), alors vous avez également votre réponse.
L'évolutivité comporte également un gros composant YAGNI. Face à l'incertitude, il s'agit d'une décision commerciale stratégique sur la construction de l'évolutivité maintenant (coûts totaux inférieurs, mais implique des coûts d'opportunité et peut ne pas être nécessaire) par rapport au report de certains travaux sur l'évolutivité (coûts totaux plus élevés si nécessaire, mais vous avez une meilleure idée de l'échelle réelle). Ce n'est pas avant tout une décision technique.
Je pense que les deux approches sont plausibles. Vous pouvez choisir de gagner en évolutivité en sacrifiant les avantages des bases de données ACID et monolithiques, ainsi que de conserver l'architecture actuelle et de sacrifier l'évolutivité et l'agilité d'une architecture plus distribuée. La bonne décision viendra du modèle commercial actuel et de la stratégie buz pour les prochaines années. Du point de vue technologique, il est difficile de le garder monolithique et de passer à une approche plus distribuée. J'analyserais le système et verrais quelles applications/modules/processus d'affaires sont plus critiques pour évoluer et évaluer les risques, les coûts et les avantages pour décider ceux qui devraient attendre ou continuer dans l'architecture monolithique.