Contratos orientados al consumidor como una forma de desarrollar un servicio.


- El secreto del éxito del proveedor es proporcionar a los consumidores productos de calidad ... oh, es decir, servicio. Bueno, y también es importante no darse el lujo de violar la compatibilidad con versiones anteriores.
Walter White


Del traductor


Que es esto


Esta es una traducción de un artículo que describe la plantilla de contratos dirigidos por el consumidor (CDC).
El original es publicado en el sitio web de Martin Fowler por Ian Robinson .


Porque es eso


En una arquitectura de microservicio, las dependencias entre servicios son una fuente de problemas. La plantilla CDC ayuda a resolver estos problemas de una manera que se adapta tanto a los desarrolladores del servicio como a sus consumidores. Fowler cita los contratos impulsados ​​por el consumidor en su artículo clave sobre la arquitectura de microservicios: microservicios .


¿Para quién es?


Este artículo será especialmente útil para equipos que desarrollan servicios para varios consumidores dentro de la misma organización y equipos que consumen dichos servicios.


Acerca de los términos


  • No traduje Contratos dirigidos por el consumidor al ruso; no traducimos Desarrollo impulsado por pruebas en una conversación. Si es necesario, puede usar la opción Contratos orientados al cliente .
  • El Contrato de proveedor y el Contrato de consumo se traducen como un contrato de proveedor y un contrato de consumo : tienen un uso sostenible en ruso.
  • Desarrollo o evolución de un servicio (evolución del servicio): refinamiento, expansión de la funcionalidad del servicio.
  • La comunidad de servicio (comunidad de servicio): el proveedor más todos los consumidores de este servicio.

Preámbulo


Este artículo analiza los problemas que surgen entre los desarrolladores y los consumidores de servicios, por ejemplo, cuando los proveedores cambian parte de su contrato, en particular, los diseños de documentos. Se describen dos estrategias para tratar con ellos:


  1. Agregar puntos de extensión.
  2. Validación "suficiente" de los mensajes recibidos.

Ambas estrategias ayudan a proteger a los consumidores de los cambios en el contrato, pero no le brindan al proveedor de servicios información:


  • la mejor manera de usar estas estrategias;
  • lo que debe hacerse a medida que se desarrolla el servicio.

El artículo describe la plantilla de contratos dirigidos por el consumidor , que permite a los proveedores comprender mejor a sus clientes y enfoca el desarrollo del servicio en lo que los consumidores necesitan.


Ejemplo de evolución del servicio


Para ilustrar algunos de los problemas que encontramos al desarrollar servicios, considere una simple ProductSearch, que le permite buscar en nuestro catálogo de productos. El resultado de la búsqueda tiene la siguiente estructura:



Figura 1: Diagrama de resultados de búsqueda


Un documento de ejemplo con resultados de búsqueda es el siguiente:


<?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á siendo utilizado actualmente por dos aplicaciones: una aplicación de marketing interna y una aplicación web de revendedor externo. Ambos clientes usan XSD para verificar los documentos recibidos antes de procesarlos. La aplicación interna utiliza los campos ID de catálogo, Nombre, Precio y Fabricante; aplicación externa: campos Id. de catálogo, nombre y precio. Ninguno de ellos utiliza el campo InStock: aunque se consideró para una aplicación de marketing, se descartó en una etapa temprana de desarrollo.


Una de las formas más comunes de desarrollar un servicio es agregar un campo a un documento para uno o más consumidores. Dependiendo de cómo se implementaron las partes del cliente y el servidor, incluso cambios simples como este pueden tener consecuencias costosas para el negocio y sus socios.


En nuestro ejemplo, después de que el servicio ProductSearch se haya estado ejecutando durante un tiempo, un segundo revendedor quiere usarlo, pero solicita agregar el campo Descripción a cada producto. Debido a la forma en que se organizan los clientes, este cambio tiene consecuencias significativas y costosas tanto para el proveedor como para los clientes existentes. El costo de cada uno de ellos depende de cómo implementemos el cambio. Hay al menos dos formas en que podemos compartir el costo de los cambios entre los miembros de la "comunidad de servicios". Primero, podríamos cambiar nuestro esquema original y requerir que cada consumidor actualice su copia del esquema para verificar correctamente los resultados de búsqueda. El costo de cambiar el sistema aquí se distribuye entre el proveedor, quien, ante tal solicitud de cambio, aún hará algunos cambios; y consumidores que no están interesados ​​en la funcionalidad actualizada. Por otro lado, podríamos agregar una segunda operación y esquema para un nuevo cliente y mantener la operación y el esquema original para los clientes existentes. Todas las mejoras ahora están del lado del proveedor, pero la complejidad del servicio y el costo de su soporte están aumentando.


Interludio: SOA quemado


Las principales ventajas de utilizar los servicios son:


  1. Mayor flexibilidad organizacional.
  2. Menores costos generales para implementar cambios.

SOA mejora la flexibilidad al colocar funciones comerciales en servicios dedicados y reutilizables. Luego, estos servicios se utilizan y se organizan para llevar a cabo los principales procesos comerciales. Esto reduce el costo de los cambios al reducir las dependencias entre los servicios, lo que les permite reconstruir y ajustar rápidamente en respuesta a cambios o eventos no planificados.


Sin embargo, una empresa puede obtener estos beneficios por completo solo si la implementación de SOA permite que los servicios evolucionen de forma independiente. Para aumentar la independencia, creamos contratos de servicio. A pesar de esto, nos vemos obligados a modificar a los consumidores tan a menudo como el proveedor, principalmente porque los consumidores dependen de la versión específica del contrato del proveedor. Al final, los proveedores comienzan a ser extremadamente cautelosos al modificar cualquier elemento del contrato; en particular, porque no tienen idea de cómo los consumidores implementan este contrato. En el peor de los casos, los consumidores están vinculados al proveedor, describiendo el esquema completo del documento como parte de su lógica interna.


Los contratos aseguran la independencia del servicio; Paradójicamente, también pueden vincular a proveedores y consumidores de manera indeseable. Sin pensar en la función y el papel de los contratos que implementamos en nuestra SOA, exponemos nuestros servicios a comunicaciones "encubiertas". La ausencia de información sobre cómo los consumidores del servicio implementan el contrato en el código, así como la falta de restricciones de implementación (tanto para el proveedor como para el consumidor), en conjunto, socavan los beneficios percibidos de SOA. En resumen, una empresa comienza a cansarse de los servicios.


Esquema de versiones


Podemos comenzar a investigar problemas contractuales y dependencias que interfieran con nuestro servicio ProductSearch versionando el esquema. El WC3 Technical Architecture Group (TAG) ha descrito una serie de estrategias de versiones que pueden ayudarnos a diseñar circuitos para nuestros servicios de manera que reduzcan los problemas de dependencia. Estas estrategias van desde ninguno excesivamente liberal, que requiere servicios para no distinguir entre diferentes versiones del esquema y aceptar todos los cambios, hasta un big bang extremadamente conservador, que requiere que el servicio arroje un error si recibe una versión inesperada del mensaje.


Ambos extremos crean problemas que no proporcionan valor para el negocio y aumentan el costo total de propiedad del sistema. Las estrategias explícitas e implícitas de "no versiones" conducen a sistemas que son impredecibles en sus interacciones, están mal desarrollados y tienen un alto costo de mejoras. Las estrategias de Big Bang, por otro lado, crean entornos de servicio altamente interconectados en los que los cambios de circuito afectan a todos los proveedores y consumidores, lo que afecta la disponibilidad, ralentiza el desarrollo y disminuye las oportunidades de ganancias.


La comunidad de servicio de nuestro ejemplo implementa efectivamente la estrategia Big Bang. Dados los costos de aumentar el valor del sistema para las empresas, está claro que los proveedores y los consumidores se beneficiarán de una estrategia de control de versiones más flexible, lo que TAG llama la estrategia de compatibilidad . Proporciona compatibilidad directa y hacia atrás de los circuitos. Con el desarrollo de los servicios, los circuitos compatibles con versiones anteriores permiten a los consumidores de nuevos circuitos aceptar instancias del circuito antiguo: un proveedor creado para procesar nuevas versiones compatibles con versiones anteriores puede, sin embargo, aceptar la solicitud en el formato del circuito antiguo. La compatibilidad directa, por otro lado, permite a los consumidores de esquemas antiguos procesar una instancia de un nuevo esquema. Este es un punto clave para los usuarios existentes de ProductSearch: si los resultados de la búsqueda se diseñaron originalmente teniendo en cuenta la compatibilidad directa, los consumidores podrían procesar las respuestas de la nueva versión sin necesidad de un desarrollo adicional.


Puntos de extensión


El mapeo de esquemas con compatibilidad hacia atrás y hacia adelante es una tarea de diseño bien entendida, mejor expresada por el patrón de extensibilidad debe ignorar (ver artículos de David Orchard y Deira Obasanjo ). La plantilla Debe Ignorar recomienda que los esquemas incluyan puntos de extensión que le permitan agregar elementos a tipos y atributos adicionales para cada elemento. La plantilla también recomienda que XML describa un modelo de procesamiento que defina qué hacen los consumidores con las extensiones. El modelo más simple requiere que los consumidores ignoren los elementos que no reconocen, de ahí el nombre de la plantilla. El modelo también puede requerir que los consumidores procesen elementos que tengan el indicador Debe entender, o den un error si no pueden entenderlos.


Aquí está nuestro resumen original del documento de resultados de búsqueda:


 <?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> 

Ahora retrocedamos en el tiempo, y desde el principio indicaremos un esquema compatible y extensible para nuestro servicio:


 <?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> 

Este diseño incluye un elemento de extensión opcional para cada producto. El elemento de extensión en sí mismo puede contener uno o más elementos del espacio de nombres de destino:



Figura 2: Esquema de resultado de búsqueda extensible


Ahora que se nos pide que agreguemos una descripción del producto, podemos publicar un nuevo esquema con un elemento Descripción adicional, que el proveedor inserta en el punto de extensión. Esto permite que ProductSearch devuelva resultados que contienen descripciones de productos, y los consumidores usan el nuevo esquema para validar todo el documento. Los consumidores que usan el esquema anterior no se romperán, aunque no procesarán el campo Descripción. Un nuevo documento de resultados de búsqueda se ve así:


 <?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> 

El esquema revisado se ve así:


 <?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> 

Tenga en cuenta que la primera versión del esquema extensible es compatible con la segunda, y la segunda es compatible con la primera. Sin embargo, esta flexibilidad tiene el costo de una mayor complejidad. Los esquemas extensibles nos permiten realizar cambios imprevistos, pero brindan características que tal vez nunca sean necesarias; mientras perdemos:


  • expresividad de un diseño simple
  • presentación clara de los datos mediante la introducción de elementos de metainformación del contenedor.

Además no discutiremos esquemas extensibles. Baste decir que los puntos de expansión nos permiten hacer cambios directos y retrocompatibles en los diagramas y documentos, sin afectar a los proveedores y consumidores del servicio. Sin embargo, expandir el esquema no nos ayuda a controlar la evolución del sistema cuando necesitamos hacer un cambio que viole el contrato.


Romper compatibilidad



- Sí, ¡nuestro equipo violó la compatibilidad con versiones anteriores en la última versión! Simplemente no pudieron resistir la ligera optimización del protocolo ... Bueno, ¡no te ofendas, cariño!
Carla Borin


Nuestro servicio ProductSearch incluye en los resultados de búsqueda un campo que indica la disponibilidad de este producto. El servicio llena este campo utilizando una llamada costosa a un antiguo sistema de inventario, una dependencia que es costosa de mantener. El proveedor desea eliminar esta dependencia, limpiar el diseño y mejorar el rendimiento general del sistema. Y preferiblemente, sin afectar a los consumidores. Al comunicarse con los consumidores, el equipo del proveedor descubre que ninguna de las aplicaciones del consumidor realmente hace nada con este campo; es decir, al ser costoso, es redundante.


Desafortunadamente, en la situación actual, si eliminamos el campo InStock de nuestro esquema extensible, eliminaremos a los consumidores existentes. Para arreglar el proveedor, debemos arreglar todo el sistema: cuando eliminamos la funcionalidad del proveedor y publicamos un nuevo contrato, cada aplicación de consumidor deberá reinstalarse con un nuevo esquema, y ​​la interacción entre los servicios se probará a fondo. El servicio de ProductSearch a este respecto no puede desarrollarse independientemente de los consumidores: el proveedor y los consumidores deben "saltar al mismo tiempo".


Nuestra comunidad de servicios está decepcionada con su evolución, porque cada consumidor tiene una dependencia "oculta", lo que refleja el contrato completo del proveedor en la lógica interna del consumidor. Los consumidores, que utilizan la validación XSD y, en menor medida, el enlace estático al esquema del documento en el código, aceptan implícitamente todo el contrato del proveedor, independientemente de su intención de utilizar solo una parte.


David Orchard da algunas pistas sobre cómo podríamos evitar este problema, refiriéndose al principio de confiabilidad : "La implementación debe ser conservadora en su comportamiento y liberal en lo que acepta de los demás". Podemos fortalecer este principio en el contexto del desarrollo del servicio al afirmar que los destinatarios del mensaje deben realizar una verificación "suficiente": es decir, solo deben procesar los datos que utilizan y deben realizar una verificación explícitamente limitada o específica de los datos que reciben, a diferencia de la validación implícitamente ilimitada Todo o nada inherente al procesamiento XSD.


Schematron


Una forma de configurar la validación del lado del consumidor es usar máscaras o expresiones regulares para las rutas a los elementos del documento, posiblemente utilizando un lenguaje de validación de estructura de árbol como Schematron . Con Schematron, cada usuario de ProductSearch puede especificar lo que esperan encontrar en los resultados de búsqueda:


 <?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> 

Las implementaciones de Schematron suelen traducir un esquema de Schematron, como este, en una transformación XSLT que el destinatario del mensaje puede aplicar al documento para validar.


Tenga en cuenta que este diagrama no contiene supuestos sobre elementos en el documento fuente que no son necesarios para la aplicación del consumidor. Por lo tanto, la validación de solo los elementos requeridos se describe explícitamente. Los cambios en el esquema del documento fuente no serán rechazados durante la validación si no violan las expectativas explícitas descritas en el esquema Schematron, incluso si elimina elementos previamente requeridos.


Aquí hay una solución relativamente fácil para nuestros problemas con el contrato y la dependencia, y esto no requiere que agreguemos elementos de metainformación implícitos al documento. Entonces, retrocedamos nuevamente en el tiempo y restauremos el circuito simple descrito al comienzo del artículo. Pero esta vez, también insistiremos en que los consumidores son libres en su comportamiento y validaremos y procesaremos solo la información que necesitan (usando esquemas de Schematron, no XSD para verificar los mensajes recibidos). Ahora que un proveedor agrega una descripción para un producto, puede publicar un esquema revisado sin afectar a los clientes existentes. De manera similar, si encuentra que el campo InStock no es verificado ni procesado por ninguno de los consumidores, el servicio puede revisar el diagrama de resultados de búsqueda, nuevamente, sin afectar a los consumidores.


Al final de este proceso, el esquema de respuesta de ProductSearch se ve así:


 <?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> 

Contratos dirigidos por el consumidor


El uso de Schematron en el ejemplo anterior lleva a algunas conclusiones interesantes sobre los contratos entre proveedores y consumidores que van más allá de la validación de documentos. En esta sección, destacamos y resumimos algunas de estas ideas y las expresamos en términos de la plantilla que llamamos contratos de consumo .


Lo primero a tener en cuenta es que los esquemas de documentos son solo una parte de lo que un proveedor de servicios debe ofrecer a los consumidores para que puedan usar su funcionalidad. Llamamos a esta información tercerizada un contrato de proveedor .


Contratos de proveedores


El contrato del proveedor describe la funcionalidad del servicio como un conjunto de elementos exportados necesarios para usar esta funcionalidad. En términos de desarrollo de servicios, un contrato es un contenedor para un conjunto de elementos exportados que describen funciones comerciales. Una lista de estos artículos incluye:


  • Esquemas de documentos . Ya hemos discutido en detalle los esquemas de documentos. Junto con las interfaces, los esquemas de documentos son parte del contrato de un proveedor, cuyo cambio es más probable a medida que se desarrolla el servicio; pero quizás por esto también son las partes con las que tenemos la mayor experiencia en implementación con diversas estrategias de desarrollo de servicios, como puntos de extensión y el uso de máscaras para rutas para documentar elementos.
  • Interfaces En su forma más simple, las interfaces del proveedor incluyen un conjunto de firmas de transacciones exportadas que un consumidor puede usar para controlar el comportamiento del proveedor. Los sistemas de mensajería generalmente exportan firmas de transacciones relativamente simples y ponen contenido empresarial dentro de los mensajes que intercambian. En tales sistemas, los mensajes recibidos se procesan de acuerdo con la semántica codificada en el encabezado o cuerpo del mensaje. 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 , . , , .


, :


/
/

Implementación


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. , ; . — — , " " .


, : , . , , , , .


Referencias


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

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


All Articles