Il existe de nombreuses solutions qui compromettent plus que je ne suis à l'aise avec. Certes, si votre cas d'utilisation est complexe, tel que le transfert d'argent entre différentes banques, des alternatives plus agréables peuvent s'avérer impossibles. Mais regardons ce que nous pouvons faire dans le scénario courant, où l'utilisation de microservices interfère avec nos transactions de base de données potentielles.
Option 1: éviter les transactions si cela est possible
Évident et mentionné auparavant, mais idéal si nous pouvons le gérer. Les composants appartiennent-ils réellement au même microservice? Ou pouvons-nous repenser le (s) système (s) de telle sorte que la transaction devienne inutile? Peut-être que l'acceptation de la non-transactionalité est le sacrifice le plus abordable.
Option 2: Utiliser une file d'attente
S'il est suffisamment certain que l'autre service réussira dans tous les domaines, nous pouvons l'appeler via une forme de file d'attente. L'élément en file d'attente ne sera récupéré que plus tard, mais nous pouvons nous assurer qu'il est en file d'attente .
Par exemple, supposons que nous voulions insérer une entité et envoyer un courrier électronique, en tant que transaction unique. Au lieu d'appeler le serveur de messagerie, nous mettons le courrier électronique dans une table.
Begin transaction
Insert entity
Insert e-mail
Commit transaction
Un inconvénient évident est que plusieurs microservices devront avoir accès à la même table.
Option 3: Effectuez le travail externe en dernier, juste avant de terminer la transaction.
Cette approche repose sur l'hypothèse qu'il est très peu probable que l'engagement de la transaction échoue.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Si les requêtes échouent, l'appel externe n'a pas encore eu lieu. Si l'appel externe échoue, la transaction n'est jamais validée.
Cette approche est limitée par le fait que nous ne pouvons émettre qu’un seul appel externe et que cela doit être fait en dernier (c’est-à-dire que nous ne pouvons pas utiliser le résultat obtenu dans nos requêtes).
Option 4: Créer des objets en attente
Comme indiqué ici , plusieurs microservices peuvent créer différents composants, chacun dans un état en attente, de manière non transactionnelle.
Toute validation est effectuée, mais rien n'est créé dans un état définitif. Une fois que tout a été créé avec succès, chaque composant est activé. Habituellement, cette opération est si simple et les probabilités que quelque chose ne va pas sont si petites que nous pourrions même préférer procéder à l'activation de manière non-transactionnelle.
Le plus gros inconvénient est probablement que nous devons tenir compte de l'existence d'éléments en attente. Toute requête sélectionnée doit déterminer s'il faut inclure les données en attente. La plupart devraient l'ignorer. Et les mises à jour sont une autre histoire.
Option 5: laisser le microservice partager sa requête
Aucune des autres options ne le fait pour vous? Alors soyons peu orthodoxes .
Selon l'entreprise, celle-ci peut être inacceptable. Je suis au courant. Ceci est peu orthodoxe. Si ce n'est pas acceptable, prenez une autre route. Mais si cela correspond à votre situation, le problème sera résolu de manière simple et puissante. Ce pourrait être juste le compromis le plus acceptable.
Il existe un moyen de transformer les requêtes de plusieurs microservices en une simple transaction de base de données.
Renvoie la requête plutôt que de l'exécuter.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
En ce qui concerne le réseau, chaque microservice doit pouvoir accéder à chaque base de données. Gardez cela à l'esprit, y compris en ce qui concerne la mise à l'échelle future.
Si les bases de données impliquées dans la transaction sont sur le même serveur, il s'agira d'une transaction normale. S'ils sont sur des serveurs différents, ce sera une transaction distribuée. Le code est le même quel que soit.
Nous recevons la requête, y compris son type de connexion, ses paramètres et sa chaîne de connexion. Nous pouvons le résumer dans une classe de commandes exécutable soignée, en gardant le flux lisible: L'appel de microservice génère une commande que nous exécutons dans le cadre de notre transaction.
La chaîne de connexion correspond à ce que nous fournit le microservice d'origine. Par conséquent, la requête est toujours considérée comme exécutée par ce microservice. Nous sommes simplement en train de l'acheminer physiquement via le microservice client. Cela fait-il une différence? Eh bien, cela nous permet de le mettre dans la même transaction avec une autre requête.
Si le compromis est acceptable, cette approche nous donne la transactionnalité simple d’une application monolithique, dans une architecture de microservice.