Pour ce deuxième article sur les problématiques à étudier avant la mise en œuvre de microservices nous traiterons de la gestion des données.
Data Management
Les services partagent-ils la même source de données ou faut-il privilégier l’approche un service = un datastore ?
Figure 4-7. Comparaison de la souveraineté des données : base de données monolithique et microservices
Chaque microservice est responsable de ces données et de leurs cohérences. C’est une dépendance externe, et elle doit être adaptée en fonction des besoins :
- Base de données relationnelle (Oracle, SQLServer)
- NoSQL (MongoDB par exemple)
Toutefois, il est tout à fait acceptable que les services partagent le même serveur de base de données physique. Mais chaque service doit être le garant de ces données ! Le problème survient quand les services partagent le même schéma, ou lisent et écrivent sur le même jeu de tables de base de données.
Il n’y a pas d’obligation de provisionner une base de données pour chaque service. Avec un SGBDR nous avons plusieurs possibilitées :
- Chaque service à son jeu de tables attribués et ne peut effectuer des opérations CRUD que sur ces tables. Un seul microservice doit avoir accès à une table de la base de données. Il est donc interdit à un service B de réaliser une opération CRUD sur les données dont est responsable le service A.
- Un service pour un schéma de la base.
- Un service : une base de données.
Puisqu’on évoque le principe de « separation of concern » au niveau entités, on peut aller plus loin encore dans cette démarche. Le CQRS (Command and Query Responsibility Segregation) est une architecture qui permet de séparer la lecture (Query) de l’écriture (Command).
Le pattern CQRS consiste à séparer le modèle d’écriture du ou des modèles de lecture. Cela répond à un constat simple : les applications ont des besoins différents en écriture et en lecture.
- La lecture doit être performante. Elle permet d’accéder à des données agrégées et filtrées nécessitant une dénormalisation.
- L’écriture nécessite des performances moindres car souvent moins importante en termes de volumétrie. Par contre, elle gère des règles métiers complexes et la cohérence entre les données.
Cette approche peux être intéressante si une opérations d’écriture nécessite des traitement couteux sur les données car elle permet de ne pas impacter les clients en lecture. Cette approche demande, en contrepartie, un effort d’architecture et de développement conséquent ! La encore, il faudra faire vos choix en fonction de vos besoins.
Gestion des transactions
Les microservices peuvent-ils partager une transaction ?
Si une opération de mon SI nécessite deux microservices, comment font-ils pour se partager la transaction et garantir la cohérence du système en cas d’erreur ? Il faudrait pouvoir faire un rollback en cas d’échec et un commit en cas de succès mais sur les deux services via une même transaction.
Cela ne posait pas de problème avec SOAP et WCF (voir le standart WS-AT). Ici, avec REST ce n’est tout simplement pas possible !
Il faut donc gérer ce problème de façon logiciel. Des patterns sont utilisables
- méthodes d’annulation
- commit à deux phases
Mais couteux à mettre en œuvre. Ce genre de pratique peux surtout être symptomatique d’un mauvais découpage fonctionnel !
La question qu’il faudrait plutôt se poser serait : Si deux microservices doivent partager / échanger des données, ne devraient-ils pas être fusionnés en un seul ?