Les contrats axés sur le consommateur comme moyen de développer un service


- Le secret du succès du fournisseur est de fournir aux consommateurs des produits de qualité ... oh, c'est-à-dire un service. Eh bien et il est également important de ne pas se laisser aller à tout sérieux avec violation de la compatibilité descendante.
Walter White


Du traducteur


Qu'est ce que c'est


Il s'agit de la traduction d'un article décrivant le modèle CDC (Consumer-Driven Contracts).
L'original est publié sur le site Web de Martin Fowler par Ian Robinson .


Pourquoi est-ce


Dans une architecture de microservices, les dépendances entre services sont source de problèmes. Le modèle CDC aide à résoudre ces problèmes d'une manière qui convient à la fois aux développeurs du service et à ses consommateurs. Fowler cite les contrats axés sur les consommateurs dans son article clé sur l'architecture des microservices: Microservices .


À qui est-il destiné?


Cet article sera particulièrement utile pour les équipes développant des services pour plusieurs consommateurs au sein d'une même organisation, et les équipes qui consomment de tels services.


À propos des termes


  • Je n'ai pas traduit les contrats axés sur les consommateurs en russe - nous ne traduisons pas le développement piloté par les tests dans une conversation. Si nécessaire, vous pouvez utiliser l'option Contrats orientés client .
  • Le contrat de fournisseur et le contrat de consommateur sont traduits comme un contrat de fournisseur et un contrat de consommateur - ils ont une utilisation durable en russe.
  • Développement ou évolution d'un service (évolution du service) - raffinement, extension des fonctionnalités du service.
  • La communauté de service (communauté de service) - le fournisseur et tous les consommateurs de ce service.

Préambule


Cet article traite des problèmes qui surviennent entre les développeurs et les consommateurs de services, par exemple lorsque les fournisseurs modifient une partie de leur contrat, en particulier la mise en page des documents. Deux stratégies pour y faire face sont décrites:


  1. Ajout de points d'extension.
  2. Validation "suffisante" des messages reçus.

Les deux stratégies aident à protéger les consommateurs des modifications du contrat, mais ne donnent pas au fournisseur de services un aperçu:


  • comment utiliser au mieux ces stratégies;
  • ce qui doit être fait à mesure que le service se développe.

L'article décrit le modèle de contrats axés sur le consommateur, qui permet aux fournisseurs de mieux comprendre leurs clients et concentre le développement du service sur les besoins des consommateurs.


Exemple d'évolution de service


Pour illustrer certains des problèmes que nous rencontrons lors du développement de services, envisagez une simple recherche de produits, qui vous permet de rechercher dans notre catalogue de produits. Le résultat de la recherche a la structure suivante:



Figure 1: Diagramme des résultats de la recherche


Un exemple de document avec des résultats de recherche est le suivant:


<?xml version="1.0" encoding="utf-8"?> <Products xmlns="urn:example.com:productsearch:products"> <Product> <CatalogueID>101</CatalogueID> <Name>Widget</Name> <Price>10.99</Price> <Manufacturer>Company A</Manufacturer> <InStock>Yes</InStock> </Product> <Product> <CatalogueID>300</CatalogueID> <Name>Fooble</Name> <Price>2.00</Price> <Manufacturer>Company B</Manufacturer> <InStock>No</InStock> </Product> </Products> 

ProductSearch est actuellement utilisé par deux applications: une application marketing interne et une application web revendeur externe. Les deux clients utilisent XSD pour vérifier les documents reçus avant de les traiter. L'application interne utilise les champs CatalogIDID, Name, Price et Manufacturer; application externe - champs CatalogIDID, Nom et Prix. Aucun d'entre eux n'utilise le champ InStock: bien qu'il ait été envisagé pour une application marketing, il a été rejeté à un stade précoce de développement.


L'une des façons les plus courantes de développer un service consiste à ajouter un champ à un document pour un ou plusieurs consommateurs. Selon la façon dont les composants client et serveur ont été mis en œuvre, même de simples changements comme celui-ci peuvent avoir des conséquences coûteuses pour l'entreprise et ses partenaires.


Dans notre exemple, après que le service ProductSearch a fonctionné pendant un certain temps, un deuxième revendeur souhaite l'utiliser, mais demande d'ajouter le champ Description à chaque produit. En raison de la façon dont les clients sont organisés, ce changement a des conséquences importantes et coûteuses pour le fournisseur et les clients existants. Le coût de chacun d'eux dépend de la façon dont nous mettons en œuvre le changement. Il existe au moins deux façons de partager le coût des changements entre les membres de la «communauté des services». Tout d'abord, nous pourrions modifier notre schéma d'origine et exiger que chaque consommateur mette à jour sa copie du schéma afin de vérifier correctement les résultats de la recherche. Le coût du changement de système ici est réparti entre le fournisseur qui, face à une telle demande de changement, fera encore quelques changements; et les consommateurs qui ne sont pas intéressés par les fonctionnalités mises à jour. D'un autre côté, nous pourrions ajouter une deuxième opération et un nouveau schéma pour un nouveau client et conserver le fonctionnement et le schéma d'origine pour les clients existants. Toutes les améliorations sont désormais du côté du fournisseur, mais la complexité du service et le coût de son support augmentent.


Interlude: Burnt SOA


Les principaux avantages de l'utilisation des services sont:


  1. Flexibilité organisationnelle accrue.
  2. Coûts globaux réduits pour la mise en œuvre des changements.

SOA améliore la flexibilité en plaçant les fonctions métier dans des services dédiés et réutilisables. Ces services sont ensuite utilisés et orchestrés pour exécuter les principaux processus métier. Cela réduit le coût des modifications en réduisant les dépendances entre les services, ce qui leur permet de reconstruire et de régler rapidement en réponse à des modifications ou à des événements imprévus.


Cependant, une entreprise ne peut pleinement tirer parti de ces avantages que si la mise en œuvre de la SOA permet aux services d'évoluer de manière indépendante. Pour accroître l'indépendance, nous créons des contrats de service. Malgré cela, nous sommes obligés de modifier les consommateurs aussi souvent que le fournisseur, principalement parce que les consommateurs dépendent de la version spécifique du contrat du fournisseur. En fin de compte, les fournisseurs commencent à être extrêmement prudents lorsqu'ils modifient un élément du contrat; en particulier, parce qu'ils ne savent pas comment les consommateurs mettent en œuvre ce contrat. Dans le pire des cas, les consommateurs sont liés au fournisseur, décrivant l'intégralité du plan du document dans le cadre de leur logique interne.


Les contrats garantissent l'indépendance du service; paradoxalement, ils peuvent également lier les fournisseurs et les consommateurs de manière indésirable. Sans penser à la fonction et au rôle des contrats que nous mettons en œuvre dans notre architecture SOA, nous exposons nos services à des communications «secrètes». L'absence d'informations sur la façon dont les consommateurs du service mettent en œuvre le contrat dans le code, ainsi que l'absence de restrictions de mise en œuvre (pour le fournisseur et le consommateur), ensemble, sapent les avantages perçus de la SOA. Bref, une entreprise commence à se lasser des services.


Gestion des versions de schéma


Nous pouvons commencer à rechercher les problèmes de contrat et les dépendances qui interfèrent avec notre service ProductSearch en versionnant le schéma. Le groupe d'architecture technique (TAG) WC3 a décrit un certain nombre de stratégies de gestion des versions qui peuvent nous aider à concevoir des circuits pour nos services de manière à réduire les problèmes de dépendance. Ces stratégies vont d'une stratégie trop libérale, qui nécessite que les services ne distinguent pas les différentes versions du schéma et n'acceptent pas toutes les modifications, à un big bang extrêmement conservateur, qui oblige le service à générer une erreur s'il reçoit une version inattendue du message.


Les deux extrêmes créent des problèmes qui n'apportent pas de valeur à l'entreprise et n'augmentent pas le coût total de possession du système. Les stratégies explicites et implicites de «pas de version» conduisent à des systèmes imprévisibles dans leurs interactions, mal développés et qui ont un coût élevé d’améliorations. Les stratégies du Big Bang, d'autre part, créent des paysages de services hautement interconnectés dans lesquels les changements de circuits affectent tous les fournisseurs et les consommateurs, nuisant à la disponibilité, ralentissant le développement et diminuant les opportunités de profit.


La communauté de service de notre exemple met en œuvre efficacement la stratégie du Big Bang. Compte tenu des coûts liés à l'augmentation de la valeur du système pour les entreprises, il est clair que les fournisseurs et les consommateurs bénéficieront d'une stratégie de contrôle de version plus flexible - ce que TAG appelle la stratégie de compatibilité . Il offre une compatibilité directe et en amont des circuits. Avec le développement des services, les schémas rétrocompatibles permettent aux consommateurs de nouveaux schémas d'accepter des instances de l'ancien schéma: un fournisseur créé pour traiter les nouvelles versions rétrocompatibles peut cependant accepter la demande dans l'ancien format de schéma. La compatibilité directe, d'autre part, permet aux consommateurs d'anciens schémas de traiter une instance d'un nouveau schéma. Il s'agit d'un point clé pour les utilisateurs existants de ProductSearch: si les résultats de la recherche étaient initialement conçus dans un souci de compatibilité directe, les consommateurs pourraient traiter les réponses de la nouvelle version sans avoir besoin de développements supplémentaires.


Points d'extension


Le mappage de schéma avec compatibilité ascendante et descendante est une tâche de conception bien comprise, mieux exprimée par le modèle d'extensibilité Must Ignore (voir les articles de David Orchard et Deira Obasanjo ). Le modèle Must Ignore recommande que les schémas incluent des points d'extension qui vous permettent d'ajouter des éléments aux types et des attributs supplémentaires pour chaque élément. Le modèle recommande également que XML décrive un modèle de traitement qui définit ce que les consommateurs font avec les extensions. Le modèle le plus simple oblige les consommateurs à ignorer les éléments qu'ils ne reconnaissent pas - d'où le nom du modèle. Le modèle peut également obliger les consommateurs à traiter les éléments qui ont l'indicateur Doit comprendre, ou donner une erreur s'ils ne peuvent pas les comprendre.


Voici notre aperçu original du document de résultats de recherche:


 <?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema> 

Revenons maintenant dans le temps et, dès le début, nous indiquerons un schéma compatible et extensible pour notre service:


 <?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name="Extension" type="Extension" /> </xs:sequence> </xs:complexType> <xs:complexType name="Extension"> <xs:sequence> <xs:any minOccurs="1" maxOccurs="unbounded" namespace="##targetNamespace" processContents="lax" /> </xs:sequence> </xs:complexType> </xs:schema> 

Cette conception comprend un élément d'extension en option pour chaque produit. L'élément d'extension lui-même peut contenir un ou plusieurs éléments de l'espace de noms cible:



Figure 2: schéma de résultats de recherche extensible


Maintenant que l'on nous demande d'ajouter une description du produit, nous pouvons publier un nouveau schéma avec un élément Description supplémentaire, que le fournisseur insère dans le point d'extension. Cela permet à ProductSearch de renvoyer des résultats contenant des descriptions de produits, et les consommateurs utilisent le nouveau schéma pour valider l'intégralité du document. Les consommateurs utilisant l'ancien schéma ne se casseront pas, bien qu'ils ne traiteront pas le champ Description. Un nouveau document de résultat de recherche ressemble à ceci:


 <?xml version="1.0" encoding="utf-8"?> <Products xmlns="urn:example.com:productsearch:products"> <Product> <CatalogueID>101</CatalogueID> <Name>Widget</Name> <Price>10.99</Price> <Manufacturer>Company A</Manufacturer> <InStock>Yes</InStock> <Extension> <Description>Our top of the range widget</Description> </Extension> </Product> <Product> <CatalogueID>300</CatalogueID> <Name>Fooble</Name> <Price>2.00</Price> <Manufacturer>Company B</Manufacturer> <InStock>No</InStock> <Extension> <Description>Our bargain fooble</Description> </Extension> </Product> </Products> 

Le schéma révisé ressemble à ceci:


 <?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="InStock" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name="Extension" type="Extension" /> </xs:sequence> </xs:complexType> <xs:complexType name="Extension"> <xs:sequence> <xs:any minOccurs="1" maxOccurs="unbounded" namespace="##targetNamespace" processContents="lax" /> </xs:sequence> </xs:complexType> <xs:element name="Description" type="xs:string" /> </xs:schema> 

Notez que la première version du schéma extensible est compatible avec la seconde, et la seconde est rétrocompatible avec la première. Cependant, cette flexibilité se fait au prix d'une complexité accrue. Les schémas extensibles nous permettent d'apporter des modifications imprévues, mais ils fournissent des fonctionnalités qui peuvent ne jamais être nécessaires; pendant que nous perdons:


  • expressivité d'un design simple
  • présentation claire des données en introduisant des éléments de méta-informations du conteneur.

De plus, nous ne discuterons pas des schémas extensibles. Il suffit de dire que les points d'extension nous permettent d'apporter des modifications directement et rétrocompatibles dans les diagrammes et les documents, sans affecter les fournisseurs et les consommateurs du service. Cependant, l'expansion du système ne nous aide pas à contrôler l'évolution du système lorsque nous devons apporter une modification qui viole le contrat.


Rompre la compatibilité



- Oui, notre équipe a violé la compatibilité descendante dans la dernière version! Ils ne pouvaient tout simplement pas résister à la légère optimisation du protocole ... Eh bien, ne soyez pas offensé, ma puce!
Carla Borin


Notre service ProductSearch inclut dans les résultats de recherche un champ indiquant la disponibilité de ce produit. Le service remplit ce champ en utilisant un appel coûteux vers un ancien système d'inventaire - une dépendance dont la maintenance est coûteuse. Le fournisseur souhaite supprimer cette dépendance, nettoyer la conception et améliorer les performances globales du système. Et de préférence, sans affecter les consommateurs. En communiquant avec les consommateurs, l'équipe des fournisseurs découvre qu'aucune des applications grand public ne fait réellement quoi que ce soit avec ce domaine; c'est-à-dire, étant cher, il est redondant.


Malheureusement, dans la situation actuelle, si nous supprimons le champ InStock de notre schéma extensible, nous casserons les consommateurs existants. Pour réparer le fournisseur, nous devons réparer l'ensemble du système: lorsque nous supprimons la fonctionnalité du fournisseur et publions un nouveau contrat, chaque application grand public devra être réinstallée avec un nouveau schéma et l'interaction entre les services devra être soigneusement testée. Le service ProductSearch à cet égard ne peut pas être développé indépendamment des consommateurs: le fournisseur et les consommateurs doivent "sauter en même temps".


Notre communauté de service est déçue de son évolution, car chaque consommateur a une dépendance «cachée», qui reflète l'intégralité du contrat du fournisseur dans la logique interne du consommateur. Les consommateurs, en utilisant la validation XSD et, dans une moindre mesure, la liaison statique au schéma de document dans le code, acceptent implicitement l'intégralité du contrat fournisseur, quelle que soit leur intention de n'utiliser qu'une partie.


David Orchard donne quelques indices sur la manière d'éviter ce problème, se référant au principe de fiabilité : "La mise en œuvre devrait être conservatrice dans son comportement et libérale dans ce qu'elle accepte des autres." Nous pouvons renforcer ce principe dans le contexte du développement des services en déclarant que les destinataires du message doivent effectuer une vérification «suffisante»: autrement dit, ils ne devraient traiter que les données qu'ils utilisent et devraient effectuer une vérification explicitement limitée ou ciblée des données qu'ils reçoivent - par opposition à une validation implicitement illimitée Tout ou rien inhérent au traitement XSD.


Schematron


Une façon de configurer la validation côté consommateur consiste à utiliser des masques ou des expressions régulières pour les chemins d'accès aux éléments de document, en utilisant éventuellement un langage de validation de structure arborescente tel que Schematron . À l'aide de Schematron, chaque utilisateur de ProductSearch peut spécifier ce qu'il s'attend à trouver dans les résultats de la recherche:


 <?xml version="1.0" encoding="utf-8" ?> <schema xmlns="http://www.ascc.net/xml/schematron"> <title>ProductSearch</title> <ns uri="urn:example.com:productsearch:products" prefix="p"/> <pattern name="Validate search results"> <rule context="*//p:Product"> <assert test="p:CatalogueID">Must contain CatalogueID node</assert> <assert test="p:Name">Must contain Name node</assert> <assert test="p:Price">Must contain Price node</assert> </rule> </pattern> 

Les implémentations Schematron traduisent généralement un schéma Schematron, tel que celui-ci, en une transformation XSLT que le destinataire du message peut appliquer au document à valider.


Notez que ce diagramme ne contient pas d'hypothèses sur les éléments du document source qui ne sont pas nécessaires à l'application grand public. Ainsi, la validation des seuls éléments requis est explicitement décrite. Les modifications apportées au schéma du document source ne seront pas rejetées lors de la validation si elles ne violent pas les attentes explicites décrites dans le schéma Schematron, même si elles suppriment les éléments précédemment requis.


Voici une solution relativement facile à nos problèmes de contrat et de dépendance, et cela ne nous oblige pas à ajouter des éléments de méta-informations implicites au document. Revenons donc dans le temps et restaurons le circuit simple décrit au début de l'article. Mais cette fois, nous insisterons également pour que les consommateurs soient libres dans leur comportement et valident et traitent uniquement les informations dont ils ont besoin (en utilisant les schémas Schematron, pas XSD pour vérifier les messages reçus). Maintenant qu'un fournisseur ajoute une description pour un produit, il peut publier un plan révisé sans affecter les clients existants. De même, s'il constate que le champ InStock n'est vérifié ou traité par aucun des consommateurs, le service peut réviser le diagramme des résultats de la recherche - à nouveau, sans affecter les consommateurs.


À la fin de ce processus, le schéma de réponse ProductSearch ressemble à ceci:


 <?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns="urn:example.com:productsearch:products" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:example.com:productsearch:products" id="Products"> <xs:element name="Products" type="Products" /> <xs:complexType name="Products"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Product" type="Product" /> </xs:sequence> </xs:complexType> <xs:complexType name="Product"> <xs:sequence> <xs:element name="CatalogueID" type="xs:int" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Price" type="xs:double" /> <xs:element name="Manufacturer" type="xs:string" /> <xs:element name="Description" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema> 

Contrats axés sur le consommateur


L'utilisation de Schematron dans l'exemple ci-dessus conduit à des conclusions intéressantes sur les contrats entre fournisseurs et consommateurs qui vont au-delà de la validation de documents. Dans cette section, nous mettons en évidence et résumons certaines de ces idées et les exprimons en termes de modèle que nous appelons les contrats de consommation .


La première chose à noter est que les systèmes de documents ne sont qu'une partie de ce qu'un fournisseur de services devrait offrir aux consommateurs afin qu'ils puissent utiliser ses fonctionnalités. Nous appelons ces informations externalisées un contrat de fournisseur .


Contrats fournisseurs


Le contrat du fournisseur décrit la fonctionnalité du service comme un ensemble d'éléments exportés nécessaires pour utiliser cette fonctionnalité. En termes de développement de services, un contrat est un conteneur pour un ensemble d'éléments exportés qui décrivent des fonctions métier. Une liste de ces éléments comprend:


  • Schémas de documents . Nous avons déjà discuté en détail des schémas de documents. Parallèlement aux interfaces, les schémas de documents font partie du contrat du fournisseur, dont le changement est le plus probable à mesure que le service se développe; mais peut-être à cause de cela, ce sont aussi les parties avec lesquelles nous avons le plus d'expérience de mise en œuvre avec diverses stratégies de développement de services, telles que les points d'extension et l'utilisation de masques pour les chemins d'accès aux éléments de document.
  • Interfaces Dans leur forme la plus simple, les interfaces de fournisseur incluent un ensemble de signatures de transaction exportées qu'un consommateur peut utiliser pour contrôler le comportement du fournisseur. Les systèmes de messagerie exportent généralement des signatures de transaction relativement simples et placent du contenu métier dans les messages qu'ils échangent. Dans de tels systèmes, les messages reçus sont traités selon la sémantique codée dans l'en-tête ou le corps du message. RPC- , , - . , , , , .
  • . , , "-" "fire-and-forget". , . , , . , «» , , , "" . , " ", , . , , .
  • . , , , , . , . Web- WS-Policy , WS-SecurityPolicy, " ", .
  • . , , , , . .

, , , , . , : , . , - , , , , . , , WS-Basic, .


, . , , , .


, ? , , (, ) , . , , , , - . .


:




, — , — . Schematron . , , . , , , , "" , . , , , - , .


, /, , ( ). , , , .


, , , , , . , , .



3:


:


  • . . .
  • . , . - , - . , , . , . , ; .
  • . , / .

Consumer-Driven Contracts


. , , , . , , . , . , Consumer-Driven Contracts .


---, . , , . ; , , .


Consumer-Driven Contracts :


  • . , . , / , / .
  • . , , .
  • . . , Consumer-Driven Contract , . , , .


, :


La quantité
Complet/
/
Complet

Implémentation


Consumer-Driven Contracts , Consumer-Driven Contracts. , , , / .


. , -. unit-, , , . Schematron WS-Policy, .


, , . Consumer-Driven Contracts , , , , . / , . , , - .



Consumer-Driven Contracts , . -, . , . Consumer-driven contracts , , — , , . "" , -, . — — , .


, , . Consumer-Driven Contracts . Consumer-driven contracts , , . , , , , . , , .



Consumer-Driven Contracts , , Consumer-Driven Contracts , . , , CDC.


Consumer-Driven Contracts , , : , , , . , , , . .


, , Consumer-Driven Contracts, , . , — : , . , , , . , , , , . , — , deprecated , .


Consumer-Driven Contracts . , , . , , «» , .


, Consumer-Driven Contracts . , - — - — , , , WS-Agreement WSLA, SLA. , ; . — — , " " .


, : , . , , , , .


Les références


  1. Ian Robinson. Consumer-Driven Contracts ( )
  2. Martin Fowler. Microservices , Decentralized Governance
  3. . , " "
  4. .

Source: https://habr.com/ru/post/fr423803/


All Articles