Il y a 16 ans -
Temps de lecture 6 minutes
Service Stateful vs. Service Stateless
Lors de la réalisation d’un service pour une architecture SOA, il arrive de devoir introduire, dans certaines situations, les notions de contexte ou de session. Nous allons voir quels sont les impacts de tels mécanismes et qu’il suffit d’appliquer la célèbre méthode KISS (Keep It Simple, Stupid) pour s’en sortir.
- Définitions des termes « contexte » et « session »
- Le contexte et la session nuisent à la compréhension et à la maintenabilité
- Le contexte et la session nuisent à la réutilisation
- La session nuit aux performances
- Où gérer le contexte ou la notion de session ?
Définitions
Que signifient les termes « contexte » et « session »? :
- le contexte d’un service est l’environnement fonctionnel (par exemple un cas d’utilisation) ou technique (par exemple une application web J2EE) dans lequel celui-ci est invoqué.
- la session permet de conserver des informations au travers de plusieurs invocations d’un même service jusqu’à la libération de cette session.
Le contexte et la session nuisent à la compréhension et à la maintenabilité
Il arrive bien souvent de rencontrer des services qui effectuent des traitements différents suivant les cas d’utilisation, le canal (web, intranet, batch, etc.) ou autres contextes métiers. Ces conditions sont souvent basées sur ce que l’on appelle des nombres magiques (ou des chaînes de caractères) qui doivent être passés en paramètre par l’appelant. Ceci peut totalement nuire à la lecture du code et à sa maintenance.
En effet, un des critères des analyseurs de code qui détermine la qualité d’un code source est la complexité cyclomatique (nombre de chemins indépendants d’un programme). Ce critère augmente avec le nombre de conditions existant dans le code. Plus le traitement est linéaire, plus il est facile à lire, à débugger, et à maintenir (par quelqu’un qui n’a pas travaillé dessus).
Dans le même esprit, utiliser dans le traitement d’un service le résultat, les données ou l’état d’un traitement effectué précédemment et stocké dans la session contribue à obscurcir le code. Ceci implique parfois de lire deux services en même temps pour réussir à comprendre dans quel cas on passe par tel ou tel morceau de code. Si un service a besoin d’une donnée particulière pour effectuer son traitement, il doit simplement être passé en paramètre de manière explicite.
Que faire quand l’envie nous prend de changer le comportement du service en fonction du contexte ?
Surtout ne paniquez pas !
La solution la plus simple (qui est souvent la meilleure) consiste tout simplement à créer deux services puisqu’il y a deux besoins différents. Si les deux services ont une partie de leur traitement en commun, nous sommes capables avec la plupart des langages d’appliquer des préceptes objets pour éviter la duplication. L’important est que les interfaces externes de ces deux services soit distinctes et clairement documentées.
Le contexte et la session nuisent à la réutilisation
Il arrive aussi que le contexte de l’appelant soit implicitement utilisé pour réaliser un traitement spécifique : « Ce service est appelé (pour l’instant !) exclusivement par notre IHM de gestion des clients alors … ». Ce genre de raisonnement va empêcher la réutilisation de ce service en l’état. Il faudra repasser par une phase de développement pour lui faire adopter le bon comportement. Il est donc très important de développer des services qui répondent à un besoin métier et non pas au besoin d’une application. La phase de définition du service est ici primordiale.
La session nuit aussi à la réutilisation. Utiliser des données de la session implique qu’un appel à un autre service a été effectué auparavant. Prenons un exemple :
- Imaginons qu’un service B utilise des données de la session. Il suppose donc qu’un appel vers le service A a déjà été effectué pour mettre les données en session. Ceci convient probablement aux services actuels.
- Imaginons maintenant qu’un nouveau service C a besoin d’appeler, lui aussi, le service B, mais que le traitement ou les données renvoyés par le service A ne l’intéressent pas.
Dans cette situation, le service C doit malgré tout effectuer un appel au service A avant l’appel au service B. Le service B n’est donc pas pleinement réutilisable. De plus, forcer cet appel peut avoir un impact en terme de performances et de temps de développement. Ce genre de situation conduit bien souvent à la duplication du service B pour satisfaire les besoins du nouvel appelant.
Remarque: Le cas présenté ci dessus (Service A, B et C) est simple, imaginez cette combinaison pour plusieurs dizaines de services, vous obtiendrez un vrai spaghetti et un taux de réutilisation des services extrêmement faible.
Pour éviter ce genre de cas, il faut privilégier le passage d’informations par paramètre. Les services sont ainsi moins contraignants et plus faciles à réutiliser, ce qui est un des objectifs de la SOA. Il faut donc être très vigilant sur la qualité et la réutilisabilité des services exposés.
La session nuit aux performances
Bien évidemment, maintenir une session requiert des ressources machines. Tant que la session n’est pas terminée, celle-ci doit être sauvegardée, ce qui peut impliquer une occupation mémoire importante. Il arrive aussi que ces données soient sauvegardées non pas en mémoire mais sur un support physique (ce qui a aussi un coût en performance). Dans un environnement clusterisé avec réplication de session, les serveurs s’échangent en permanence des informations pour synchroniser les sessions. Dans toutes ces situations, la gestion et la sauvegarde de la session entraînent des traitements supplémentaires, coûteux en performance.
Où gérer le contexte ou la notion de session ?
Qui mieux que l’appelant sait dans quel contexte il se trouve ? L’appelé ne devrait jamais être influencé par le contexte ou les données de la session.
La notion de session ne devrait être implémentée que dans les zones qui sont déjà spécifiques à un besoin particulier. L’endroit classique où apparaîssent les notions de contexte et de session est l’IHM. Une autre possibilité existe dans une architecture SOA : un orchestrateur de processus (BPM) peut être amené à maintenir un contexte ou une session entre plusieurs appels de services quand il intègre dans ses traitements des activités humaines.
Introduire des services contextuels (ou stateful) dans une architecture SOA ne doit pas se faire à la légère et peut grièvement impacter la réutilisabilité, la maintenance et les performances des services.
En résumé, il faut donc :
- définir des services simples qui répondent à un seul besoin,
- utiliser le passage d’informations explicite par paramètres
Bien que la mise en place d’une SOA nous donne de l’agilité, il faut garder en tête qu’une bonne définition des services est importante. En définissant des services réutilisables, nous évitons de passer notre temps à faire, défaire et refaire les mêmes services. On peut ainsi se concentrer sur les services qui répondent aux nouveaux besoins métiers et apporte une réelle valeur ajoutée.
Commentaire
3 réponses pour " Service Stateful vs. Service Stateless "
Published by TINE , Il y a 16 ans
Bonjour,
Tout d’abord, je voudrais vous remercier pour la pertinence des informations et leur qualité : mon blog préféré pour suivre les nouvelles et solutions SOA.
Voici ma réaction à cet article.
Dans toutes les architectures sur lesquelles j’ai personnellement traou intervenu pour différentes raisons, la notion de service (dénué de toute connotation Web service et/ou SOA) a toujours été une abstraction stateless. Il y a plein d’autres choses en amont qui sont stateful mais jamais les services.
Mais bon, puisque le faux débat est lancé je me permets de faire quelques remarques.
Dans le texte on peut lire:
« Ces conditions sont souvent basées sur ce que l’on appelle des nombres magiques (ou des chaînes de caractères) qui doivent être passés en paramètre par l’appelant. Ceci peut totalement nuire à la lecture du code et à sa maintenance. »
Faux : étant donné que ces nombres ne sont jamais manipulés directement mais au travers d’une abstraction de haut niveau qui expose une API propre et lisible et qu’on appelle une session.
Le texte continue avec : « En effet, un des critères des analyseurs de code … »
Tel que c’est formulé, on peut comprendre que le code va inclure des conditions portant sur ces nombres magiques. Je pense que vous vouliez parler du code qui permet la récupération des données dans la session. Ce code va contenir des conditions pour savoir si les données sont disponibles ou non dans le contexte.
Mise à part la formulation stylistique, cet argument n’est pas correct. Car, chaque service ne va pas dire « si données dispo alors ok sinon appeler le service ad hoc ». Il va tout simplement appeler une méthode qui va le faire pour lui. Les différents services ignorent alors complètement qu’ils dépendent de données en provenance de la session ou suite à un appel de service. L’autre façon de faire est (comme vous l’écrivez) de passer directement les données extraites du contexte comme argument au service.
Remarque sur le paragraphe « Que faire quand l’envie nous prend de changer… »
La solution est possible pour des petits exemples ou pour écrire des bouquins. La réalité est qu’il n’est pas toujours possible d’associer une méthode pour une situation abstraite donnée (combinaison de plusieurs valeurs possibles des données contenues dans le contexte). Car il y aura explosion combinatoire.
Remarque sur le paragraphe « Le contexte et la session nuisent à la réutilisation »
Dans le texte, on peut lire : « Il est donc très important de développer des services qui répondent à un besoin métier et non pas au besoin d’une application. »
Je crois que l’auteur divague et qu’il ne mesure pas la signification de ce qu’il écrit. Je ne sais pas si je dois vous relancer pour faire la distinction entre service métier, service applicatif, service fonctionnel, service technique voire même service logique.
Et pour finir l’exemple est mauvais car le problème qu’il fait ressortir se pose également avec une conception stateless. Je m’explique :
Supposons que le service B n’utilise pas la session pour récupérer ses données mais qu’il les reçoit en paramètre (comme c’est préconisé un peu plus loin). D’après votre exemple, ces données sont fournies par le service A. Donc c’est à la charge de l’appelant de B de les lui fournir en entrée. Comment il faut faire, il faut appeler A. Que cet appelant soit un autre service C ou n’importe qui, ce sera à lui d’appeler A pour les fournir à B.
Le supposé problème existe donc quelque soit la conception envisagée : stateless / stateful.
Mais en réalité, il n’y aucun problème de réutilisation car lorsqu’un service est défini il déclare son contrat et voilà.
Techniquement parlant, le vrai problème des services stateful est la scalabilité.
H. TINE
Published by Manuel Eveno , Il y a 16 ans
Tout d’abord, merci pour cette longue réaction. Je vais essayer de vous répondre point par point :
Tout d’abord, je ne suis pas sûr que ce soit un faux débat. On rencontre encore régulièrement des projets qui compliquent inutilement les services, il est toujours bon de faire quelques rappels ;-)
Concernant les nombres magiques, même s’ils sont manipulés au travers d’une session, il n’en reste pas moins que pour accéder à la session on utilise des clés. Ces clés sont des nombres magiques. L’utilisation de la session risque aussi de créer une dépendance entre les services qui manipulent les mêmes données de la session.
Au sujet de la complexité cyclomatique, je mettais ici en relief le fait que, puisque le service effectue des traitements différents en fonction de « nombres magiques » passés en paramètre, il cumule en réalité des traitements qui devraient être réalisés via plusieurs services. Le traitement du service devient donc plus complexe à déchiffrer (il faut suivre l’imbrication des if then else). De plus, que le service fasse ce test ou invoque un service qui le fait pour lui, le service reste lié aux données de la session et c’est ça qui pose problème (j’en reparle plus loin).
Concernant votre remarque sur le paragraphe « Que faire quand l’envie nous prend de changer … », il faut, bien évidemment, garder une certaine mesure. Je préconisais de découper le service en deux quand les traitements métiers effectués sont relativement dissociables. Il est clair qu’il ne faut pas tomber dans le travers de créer un service pour chaque valeur possible des paramètres.
A propos de votre remarque sur le paragraphe « Le contexte et la session nuisent à la réutilisation », je parlais ici de l’application appelante. Il ne faut pas effectuer des traitements en fonction de l’application appelante mais bien en fonction du besoin tel qu’il a été (ou devrait être) spécifié.
Voici un exemple :
Le service getContrats() est réalisé pour l’application web de gestion du compte mise à disposition des clients. Il a été décidé que dans cette application web, il ne serait possible de voir que certains types de contrat. Le risque ici est de se dire que le service getContrats() puisqu’il ne va (pour l’instant) être appelé que par cette application web, ne retournera que les contrats du bon type. On commet ici une erreur en rendant le service spécifique au besoin d’une application.
Au sujet de l’exemple, il est probable que le service B n’aie pas besoin de la totalité des données mis en session par le service A, il n’aura probablement besoin qu’une clé ou d’un sous ensemble des données. En lui donnant la possibilité de les passer en paramètre, il peut par exemple mettre ces données en cache à la connexion de l’utilisateur (si c’est une application web).
Le fait est qu’ici on a le choix alors que dans l’approche stateful, il faut obligatoirement passer par l’appel au service A.
Pour finir, il est vrai que les services stateful entraînent bien souvent des problèmes de performance et, dans le cas d’environnement répliqué, de potentiel problème de synchronisation.