Consumer-Driven Contracts as a way to develop a service
- The secret of supplier success is to provide consumers with a quality product ... oh, that is, service. Well, it is also important not to indulge in all serious ones with backward compatibility.
From the translator
What is it
This is a translation of an article describing Consumer-Driven Contracts (CDC).
The original is published on the Martin Fowler website by Jan Robinson .
Why is it
In the microservice architecture, dependencies between services are a source of problems. The CDC template helps to solve these problems in a way that suits both the developers of the service and its customers. Fowler refers to Consumer-Driven Contracts in his key article on microservice architecture: Microservices .
For whom is it
The article will be especially useful for teams developing services for several consumers within one organization, and teams-consumers of such services.
- I did not translate Consumer-Driven Contracts into Russian - we do not translate in conversation Test-Driven Development. If necessary, you can use the option Consumer-oriented .
- Provider Contract and Consumer Contract are translated as supplier contract and consumer contract - they have sustainable use in Russian.
- Development or evolution of service (service evolution) - refinement, expansion of the functionality of the service.
- The service community is the supplier plus all the consumers of this service.
This article discusses the problems that arise between developers and consumers of services, for example, when suppliers change part of their contract, in particular, the document schemes. Two strategies for dealing with them are described:
- Adding extension points.
- "Sufficient" validation of received messages.
Both strategies help protect consumers from contract changes, but do not give the service provider an understanding of:
- how best to use these strategies;
- what needs to be done as the service develops.
The article describes the template Consumer-Driven Contracts , which allows suppliers to better understand their customers and focuses the development of the service on what consumers need.
Example of service evolution
To illustrate some of the problems we face when developing services, consider a simple ProductSearch that allows you to search our product catalog. The search result has the following structure:
Figure 1: Search result layout
A sample search results document looks like this:
<Productsxmlns="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>
The ProductSearch service is currently used by two applications: an internal marketing application and an external reseller web application. Both clients use XSD to verify received documents before processing them. The internal application uses the fields CatalogueID, Name, Price and Manufacturer; external application - CatalogueID, Name and Price fields. None of them use the InStock field: although it was considered for a marketing application, it was dropped early in the development.
One of the most common ways to develop a service is to add a field to a document for one or several consumers. Depending on how the client and server parts were implemented, even simple changes like this can have costly consequences for the business and its partners.
In our example, after the ProductSearch service has been in operation for some time, the second reseller wants to use it, but asks to add the Description fieldto each product. Because of how customers are structured, this change has significant and costly implications for both the supplier and existing customers. The cost of each of them depends on how we make the change. There are at least two ways in which we can distribute the cost of change between members of the “service community”. First, we could change our original schema and require each consumer to update their copy of the schema in order to properly validate the search results. The cost of changing the system here is shared between the supplier, who, faced with such a change request, will still make some changes; and consumers who are not interested in the updated functionality. On the other hand, we could add a second operation and scheme for a new customer and keep the original operation and scheme for existing customers. All improvements are now on the side of the supplier, but the complexity of the service and the cost of its support are increasing.
Interlude: tired SOA
The main advantages of using services are:
- Increased organizational flexibility.
- Reducing the total cost of implementing changes.
SOA increases flexibility by placing business functions in dedicated, reusable services. These services are then used and orchestrated to perform core business processes. This reduces the cost of change by reducing dependencies between services, allowing them to quickly reorganize and tune in response to changes or unplanned events.
However, a business can fully realize these benefits only if the implementation of SOA allows services to evolve independently of each other. To increase independence, we create service contracts. Despite this, we are forced to refine consumers as often as the supplier, mainly because consumers depend on the specific version of the supplier’s contract. In the end, suppliers are beginning to be very careful about changing any element of the contract; in particular, because they have no idea how consumers implement this contract. In the worst case, consumers are tied to the supplier, describing the entire document scheme as part of their internal logic.
Contracts provide services independence; paradoxically, they can also bind suppliers and consumers in undesirable ways. Without thinking about the function and role of contracts that we implement in our SOA, we subject our services to “hidden” communication. The lack of any information about how service consumers implement the contract in the code, as well as the lack of restrictions on implementation (for both the supplier and the consumer), collectively undermine the perceived benefits of SOA. In short, the company begins to tire of services.
We can start researching contract and dependency issues that interfere with our ProductSearch service with versioning the schema. The WC3 Technical Architecture Group (TAG) described a number of version control strategies that can help us develop schemas for our services in ways that reduce dependency problems. These strategies range from overly liberal none , which requires services not to distinguish between different versions of the scheme and accept all changes, to an extremely conservative big bang , which requires the service to issue an error if it receives an unexpected version of the message.
Both extremes create problems that do not provide business value and increase the total cost of ownership of the system. Explicit and implicit "no version" strategies lead to systems that are unpredictable in their interactions, poorly developed, and have a high cost of refinement. On the other hand, the big bang strategies create tightly coupled service landscapes in which changes to the scheme affect all suppliers and consumers, worsening accessibility, slowing development and reducing opportunities for profit.
The “service community” of our example effectively implements the big bang strategy. Given the cost to increase the value of the system for business, it is clear that suppliers and consumers will benefit from a more flexible version management strategy - what TAG calls a compatibility strategy .. It provides forward and backward compatibility schemes. With the development of services, backward-compatible schemes allow consumers to accept instances of the old scheme: the provider created to handle backward compatible new versions can, however, accept a request in the format of the old scheme. On the other hand, direct compatibility allows consumers of old schemes to process an instance of a new scheme. This is a key point for existing ProductSearch users: if the search results schema was originally made with direct compatibility in mind, consumers could handle the responses of the new version without needing to be improved.
Backward and forward compatibility mapping is a well-understood design task, best expressed by the Must Ignore extensibility pattern (see David Orchard and Deira Obasanjo’s articles ). Must Ignore Templaterecommends that schemas include extension points that allow elements to be added to types and additional attributes for each element. The template also recommends that XML describe a processing model that defines what consumers do with extensions. The simplest model requires consumers to ignore elements that they do not recognize - hence the name of the template. The model may also require consumers to process elements that have the “Must Understanding” flag, or give an error if they cannot understand them.
Here is our initial document layout with search results:
<xs:schemaxmlns="urn:example.com:productsearch:products"xmlns:xs="http://www.w3.org/2001/XMLSchema"elementFormDefault="qualified"targetNamespace="urn:example.com:productsearch:products"id="Products"><xs:elementname="Products"type="Products" /><xs:complexTypename="Products"><xs:sequence><xs:elementminOccurs="0"maxOccurs="unbounded"name="Product"type="Product" /></xs:sequence></xs:complexType><xs:complexTypename="Product"><xs:sequence><xs:elementname="CatalogueID"type="xs:int" /><xs:elementname="Name"type="xs:string" /><xs:elementname="Price"type="xs:double" /><xs:elementname="Manufacturer"type="xs:string" /><xs:elementname="InStock"type="xs:string" /></xs:sequence></xs:complexType></xs:schema>
Let's now roll back in time, and from the very beginning let us specify a compatible, expandable scheme for our service:
<xs:schemaxmlns="urn:example.com:productsearch:products"xmlns:xs="http://www.w3.org/2001/XMLSchema"elementFormDefault="qualified"targetNamespace="urn:example.com:productsearch:products"id="Products"><xs:elementname="Products"type="Products" /><xs:complexTypename="Products"><xs:sequence><xs:elementminOccurs="0"maxOccurs="unbounded"name="Product"type="Product" /></xs:sequence></xs:complexType><xs:complexTypename="Product"><xs:sequence><xs:elementname="CatalogueID"type="xs:int" /><xs:elementname="Name"type="xs:string" /><xs:elementname="Price"type="xs:double" /><xs:elementname="Manufacturer"type="xs:string" /><xs:elementname="InStock"type="xs:string" /><xs:elementminOccurs="0"maxOccurs="1"name="Extension"type="Extension" /></xs:sequence></xs:complexType><xs:complexTypename="Extension"><xs:sequence><xs:anyminOccurs="1"maxOccurs="unbounded"namespace="##targetNamespace"processContents="lax" /></xs:sequence></xs:complexType></xs:schema>
This scheme includes an additional expansion element for each product. The extension element itself can contain one or more elements from the target namespace:
Figure 2: Extensible search result schema
Now, when we receive a request to add a product description, we can publish a new schema with an additional Description element , which the supplier inserts into the extension point. This allows the ProductSearch service to return results containing product descriptions, and consumers use the new schema to validate the entire document. Consumers using the old scheme will not break, although they will not process the Description field. The new document with the search result looks like this:
<Productsxmlns="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>
The revised scheme looks like this:
<xs:schemaxmlns="urn:example.com:productsearch:products"xmlns:xs="http://www.w3.org/2001/XMLSchema"elementFormDefault="qualified"targetNamespace="urn:example.com:productsearch:products"id="Products"><xs:elementname="Products"type="Products" /><xs:complexTypename="Products"><xs:sequence><xs:elementminOccurs="0"maxOccurs="unbounded"name="Product"type="Product" /></xs:sequence></xs:complexType><xs:complexTypename="Product"><xs:sequence><xs:elementname="CatalogueID"type="xs:int" /><xs:elementname="Name"type="xs:string" /><xs:elementname="Price"type="xs:double" /><xs:elementname="Manufacturer"type="xs:string" /><xs:elementname="InStock"type="xs:string" /><xs:elementminOccurs="0"maxOccurs="1"name="Extension"type="Extension" /></xs:sequence></xs:complexType><xs:complexTypename="Extension"><xs:sequence><xs:anyminOccurs="1"maxOccurs="unbounded"namespace="##targetNamespace"processContents="lax" /></xs:sequence></xs:complexType><xs:elementname="Description"type="xs:string" /></xs:schema>
Please note that the first version of the extensible scheme is compatible with the second, and the second is backward compatible with the first. However, this flexibility comes at the cost of increased complexity. Expandable schemes allow us to make unforeseen changes, but they provide opportunities that may never be needed; at the same time we lose:
- expressiveness coming from simple design
- clear presentation of data by entering the meta-information elements of the container.
Further we will not discuss expandable schemas. Suffice it to say that expansion points allow us to make directly and backward compatible changes in charts and documents, without affecting service providers and consumers. However, the expansion of the scheme does not help us manage the evolution of the system when we need to make a change that violates the contract.
- Yes, our team violated backward compatibility in the latest release! Just could not resist a small optimization of the protocol ... Well, do not be offended, baby!
Our ProductSearch service includes a field in the search results indicating the availability of this product. The service fills this field using an expensive call to an ancient inventory system - a dependency that is expensive to maintain. The vendor would like to remove this dependency, clean the design and improve the overall performance of the system. And preferably, without affecting consumers. By communicating with consumers, the supplier team finds out that none of the consumer applications actually do anything with this field; that is, being expensive, it is redundant.
Unfortunately, in the current situation, if we remove the InStock field from our extensible scheme, we will break existing customers. To fix the supplier, we need to fix the entire system: when we remove the functionality from the supplier and publish a new contract, each consumer application will need to be reinstalled with a new scheme, and the interaction between services will be thoroughly tested. The ProductSearch service in this regard cannot develop independently of consumers: the supplier and consumers must "jump at the same time."
Our service community is disappointed in its evolution, because every consumer has a “hidden” dependency, which reflects the entire supplier contract in the internal logic of the consumer. Consumers, using XSD validation and, to a lesser extent, static binding to the document scheme in the code, implicitly accept the entire supplier contract, regardless of their intention to use only a part.
David Orchard gives some clues as to how we could avoid this problem, referring to the principle of reliability : "The implementation must be conservative in its behavior and liberal in what it takes from others." We can strengthen this principle in the context of service development, stating that message recipients must perform “sufficient” verification: that is, they must process only the data they use, and must perform explicitly limited or targeted verification of the data they receive - as opposed to implicitly unlimited validation “All or nothing” inherent in XSD processing.
One of the ways we can customize consumer-side validation is to use masks or regular expressions for paths to document elements, possibly using the tree structure validation language, such as Schematron . Using Schematron, each user of the ProductSearch service can specify what he expects to find in the search results:
<schemaxmlns="http://www.ascc.net/xml/schematron"><title>ProductSearch</title><nsuri="urn:example.com:productsearch:products"prefix="p"/><patternname="Validate search results"><rulecontext="*//p:Product"><asserttest="p:CatalogueID">Must contain CatalogueID node</assert><asserttest="p:Name">Must contain Name node</assert><asserttest="p:Price">Must contain Price node</assert></rule></pattern>
Schematron implementations typically convert a Schematron schema, such as this, to an XSLT transform that the message recipient can apply to the document for validation.
Note that this schema does not contain assumptions about elements in the source document that are not needed by the consumer application. Thus, the validation of only the required elements is explicitly described. Changes to the schema of the original document will not be rejected during validation if they do not violate the explicit expectations described in the Schematron schema, even if it is the removal of previously required elements.
Here is a relatively easy solution to our problems with contract and dependency, and this does not require us to add implicit meta-information elements to the document. So let's roll back again in time and restore the simple scheme described at the beginning of the article. But this time we will also insist that consumers are free in their behavior and validate and process only the information that they need (using Schematron schemes, and not XSD to check received messages). Now, when a supplier adds a description for a product, he can publish a revised scheme without affecting existing consumers. Similarly, by finding that the InStock field is not being checked or processed by any of the consumers, the service can revise the search result schema — again, without affecting consumers.
At the end of this process, the ProductSearch response schema looks like this:
<xs:schemaxmlns="urn:example.com:productsearch:products"xmlns:xs="http://www.w3.org/2001/XMLSchema"elementFormDefault="qualified"targetNamespace="urn:example.com:productsearch:products"id="Products"><xs:elementname="Products"type="Products" /><xs:complexTypename="Products"><xs:sequence><xs:elementminOccurs="0"maxOccurs="unbounded"name="Product"type="Product" /></xs:sequence></xs:complexType><xs:complexTypename="Product"><xs:sequence><xs:elementname="CatalogueID"type="xs:int" /><xs:elementname="Name"type="xs:string" /><xs:elementname="Price"type="xs:double" /><xs:elementname="Manufacturer"type="xs:string" /><xs:elementname="Description"type="xs:string" /></xs:sequence></xs:complexType></xs:schema>
Using Schematron in the example above leads to some interesting conclusions about contracts between suppliers and consumers, beyond the scope of document verification. In this section, we isolate and summarize some of these ideas and express them in terms of the pattern that we call Consumer Contracts .
The first thing to note is that document schemes are only part of what the service provider has to offer consumers so that they can use its functionality. We call this customer-specific information contract supplier .
A supplier contract describes the functionality of a service as a set of exported items necessary to use this functionality. From the point of view of the development of the service, the contract is a container for a set of exported items describing business functions. The list of these elements includes:
- Document schemes . We have already discussed document schemes in detail. Along with interfaces, document schemes are parts of a supplier's contract, which are most likely to change as the service develops; but perhaps because of this, they are also the parts with which we have the most implementation experience with different strategies for developing services, such as extension points and using masks for paths to document elements.
- Interfaces . In its simplest form, supplier interfaces include a set of exported activity signatures that a consumer can use to control the behavior of a supplier. Messaging systems typically export relatively simple transaction signatures and put business content inside the messages they exchange. In such systems, received messages are processed in accordance with the semantics encoded in the header or body of the message. RPC-like services, on the other hand, encode most of their business semantics in the signatures of their operations. In any case, consumers depend on a certain part of the supplier’s interface, and therefore we must take the interface into account when developing our service.
- Dialogues. Providers and consumers exchange messages in sequences that make up one or more messaging patterns, such as request-response or fire-and-forget. During the dialogue, the consumer can expect the supplier to be in some particular context when sending and receiving messages. For example, a hotel reservation service may offer consumers the opportunity to reserve a room at the beginning of a dialogue, as well as confirm the reservation and make a deposit in subsequent exchanges. The consumer here can reasonably expect the service to “remember” the details of the reservation, rather than requiring the parties to repeat the entire “conversation” at each stage of the process. As the service develops, the set of "interactive gambits" available to the supplier and the consumer can change. Thus, the dialogues are candidates to be considered part of the supplier’s contract.
- Policies . In addition to exporting document schemes, interfaces, and dialogs, suppliers can declare and apply specific usage requirements that determine how other elements of a contract can be implemented. Most often, these requirements relate to security contexts and transactions in which the consumer can use the functionality of the service. Web services can express this using the general WS-Policy model and additional specifications, such as WS-SecurityPolicy, but in the context of “policies as candidates for inclusion in a supplier contract” that we consider, our policy definition is not tied to a specific specification or implementation.
- Characteristics of service quality . The benefits that business service providers and consumers bring to business are often evaluated in the context of the specific characteristics of the quality of service, such as availability, latency and throughput. We must take these characteristics into account as likely components of a supplier’s contract and take them into account in our service development strategies.
This definition of a contract is slightly broader than the one we usually mean when it comes to services, but it is useful from the point of view of service development. We should not consider it exhaustive with regard to the types of elements that a supplier contract may contain: it is simply a logical set of elements that are candidates for inclusion in a service development strategy. From a logical point of view, this set of candidate elements is open, but in practice, internal or external factors, such as compatibility requirements or platform constraints, may limit the type of elements that a contract may contain. For example, a service contract that matches the WS-Basic profile will not contain policies.
Despite any such restrictions, the scope of the contract is determined simply by the connectivity of its elements. A contract may contain many elements and be broad in scope, or focus only on a few, as long as this is enough to describe its functionality.
How do we decide whether to include an element in a supplier’s contract? We do this by asking ourselves if any of our consumers can formulate (for example, in the form of tests) one or several expectations regarding the functionality described by the element. We have already seen from our example of service how consumers may be interested in parts of the document schema exported by the service, and how they can claim that their expectations regarding this contract element are still met. Therefore, the document scheme is part of our supplier contract.
Supplier contracts have the following characteristics:
- Closed and full . Supplier contracts express service functionality through the full range of exported items available to consumers, and as such, are closed and complete in terms of the functionality available to use.
- The only and mandatory . Supplier contracts are the only and binding description of the functionality of the service.
- Limited stability and immutability . The supplier's contract is stable and unchanged for a limited period and / or scope (see the section "Valid Data in Bounded Space and Time" in the article by Pat Helland Data on Outside vs. Data on the Inside ). Vendor contracts typically use some form of versioning to distinguish between consumers of different contract versions.
Once we have decided to take into account the expectations of consumers regarding the schemes that we disclose when developing our service - and we believe that our supplier knows about them - then we need to import these consumer expectations into the supplier. The Schematron statements in our example are very similar to such tests. When used by the supplier, tests can help ensure that the supplier continues to fulfill its obligations to customers. By implementing these tests, the provider gains a better understanding of how he can develop the format of the messages he creates without breaking the functionality used by the service community. And where the proposed change affects one or more consumers,
In our example, we can say that the set of statements / tests generated by all consumers expresses the mandatory structure of messages for a certain period (that is, until tests are relevant). If the supplier possessed this set of claims, he could ensure that every message he sent was valid for each consumer, because the set of statements was valid and complete.
Summarizing this structure, we can distinguish what we have already called the supplier contract, from the individual contractual obligations that arise between the supplier and the consumer, which we will now call consumer contracts . When the supplier accepts reasonable expectations expressed by the consumer, he concludes a contract with the consumer.
Figure 3: Consumer Contracts
Consumer contracts have the following characteristics:
- Open and incomplete . Consumer contracts are open and incomplete regarding service functionality. They express a subset of service capabilities in terms of customer expectations.
- Plural and non-binding. The number of consumer contracts is proportional to the number of service consumers, and each of them is not binding for the entire set of supplier obligations. The non-binding nature of customer-supplier relationships is one of the key features that distinguish a service-oriented architecture from a distributed application architecture. Consumers should understand that other consumers of the service can use it in ways that are completely different from their own. Consumers can develop at different speeds and demand changes in the supplier, which can potentially break the dependencies and expectations of other consumers. The consumer cannot foresee how or when another consumer will violate the supplier’s contract; a client in a distributed application has no such problems.
- Limited stability and immutability . Like supplier contracts, consumer contracts are valid for a specific period of time and / or scope.
Consumer contracts allow us to think about business value at any time during a supplier’s life cycle. Expressing expectations from a supplier's contract, consumer contracts effectively determine which parts of a supplier's contract currently have business value and which are not. This leads us to speculate that service communities can benefit from the fact that they will immediately specify a contract from the point of view of consumers. From this point of view, supplier contracts arise to meet the expectations and requirements of consumers. To reflect the derivative nature of this new contractual arrangement, we call such supplier contracts Consumer-Driven Contracts or derivative contracts .
The derivative nature of these supplier-for-consumer contracts adds a heterogeneous aspect to the relationship between the service provider and its customer. That is, suppliers are subject to a commitment that arises beyond their borders. This in no way affects the fundamentally autonomous nature of their implementation; it simply makes it clear that services are successful only when they are used.
Consumer-Driven Contracts have the following characteristics:
- Closed and full . The contract is closed and completed in relation to the entire set of functions required of it by its existing customers. A contract is a mandatory set of elements necessary to support consumer expectations / tests, while these expectations / tests are relevant for consumer applications.
- The only and non-binding . The contract is the only, but not binding, description of the functionality of the service, since it is based on combining the existing expectations of consumers.
- Limited stability and immutability . The contract is stable and unchanged in relation to a specific set of consumer contracts. In other words, we can validate the Consumer-Driven Contract only against a specific set of consumer contracts, which effectively limits the forward and backward compatibility of the contract in time and space. Contract compatibility remains stable and unchanged for a specific set of consumer contracts and expectations, but can be changed as expectations come and go.
Summary table of contract characteristics
The following table lists the characteristics of the three types of contracts described in this article:
|the contract||Is full||Is required||Action restriction|
|Supplier||Is closed||Full||One||Binding||Time / Area|
|Consumer||Is open||Not complete||Lot||Non binding||Time / Area|
|For consumers||Is closed||Full||One||Non binding||Consumers|
The Consumer-Driven Contracts template recommends building service communities using consumer contracts and Consumer-Driven Contracts. However, the template does not indicate which forms or structures should accept these contracts, nor does it define how consumers' expectations / tests are communicated to the supplier and checked in its life cycle.
Contracts can be expressed and structured in several ways. In its simplest form, consumer expectations can be recorded in a spreadsheet or similar document and implemented during the design, development and testing of the supplier application. By going a little further and having done unit tests that confirm every expectation, we can guarantee that contracts are described and checked automatically with each build. In more complex implementations, expectations can be expressed as Schematron statements or via the WS-Policy, and are evaluated at run time in service input and output handlers.
As is the case with the contract structure, we have several options when it comes to the way of communication between suppliers and consumers. Since the Consumer-Driven Contracts template is not tied to an implementation, we could, if the organizational structure allows, transfer expectations by simply communicating with other teams or using email. If the number of expectations and / or consumers is too large, we can consider automating this process. Whatever the mechanism, most likely, these communications will be carried out before any talk about the business functions of the service.
Consumer-Driven Contracts offer two significant benefits when it comes to developing services. First, they focus the specification and implementation of the service around business benefits. A service has business value only to the extent it is consumed. Consumer-driven contracts link the development of the service with business benefits, denoting the value of contract elements exported by service consumers — what consumers require from suppliers to do their work. As a result, suppliers put out a "thrifty" contract that clearly corresponds to the business goals implemented by consumers. Change - the development of service - occurs only when consumers express a clear need.
Of course, the ability to start with a minimum set of requirements and develop our service at the request of consumers suggests that we are able to modify, deploy and manage the service in a controlled and effective manner. Here is a Consumer-Driven Contracts template.provides a second key advantage. Consumer-driven supplier contracts provide us with the subtle understanding and quick feedback needed to plan for changes and evaluate their impact on applications that are already in use. In practice, this allows us to target individual consumers and provide them with incentives to abandon the expectations that prevent us from making changes that violate direct and reverse compatibility when these changes have matured. Building a service on the basis of consumer contracts, we fill it with a repository of knowledge and a feedback mechanism on which we can rely during the operation of the service.
Risks and restrictions
In this article, we identified the motive for introducing Consumer-Driven Contracts into the service landscape, and then described how the Consumer-Driven Contracts pattern takes into account the factors determining the development of the service. In conclusion, we will discuss the scope of applicability of the template, as well as some problems that may arise during the implementation of consumer contracts and CDC.
The Consumer-Driven Contracts pattern is applicable in the context of both a single organization and a closed, closely related service community: more specifically, in an environment where suppliers can have a certain impact on how consumers enter into contracts with them. Regardless of how easy the mechanism for communicating and presenting expectations and commitments is, suppliers and consumers need to know, accept and use an agreed set of channels and conventions. This inevitably adds a level of complexity and protocol dependency to an already complex service infrastructure.
We have suggested that systems based on Consumer-Driven Contracts are better able to manage intermittent changes. But we are not going to assume that the pattern is a panacea for a compatibility issue: when all is said and done, incompatibility is still incompatibility. However, we believe that the pattern provides an insight into what this violation actually represents, and this understanding can serve as the basis for the service’s version control strategy. Moreover, as we have already discussed, the service communities that implement the template better predict the consequences of service changes. In particular, the supplier team can more effectively plan their change strategies — perhaps by marking contract elements as deprecated for a specific period,
Consumer-Driven Contracts do not necessarily reduce dependencies between services. Loosely coupled services are relatively independent of each other, but nevertheless remain connected. However, what the template does is that it brings to light some of these residual “hidden” dependencies, so that suppliers and consumers can better negotiate and manage such dependencies.
We discussed how consumer contracts and consumer-driven contracts express business values. But we must clearly state that we do not consider such contracts as an index or an indicator of business value - they are not a business metric - and, despite some superficial similarities with specifications such as the WS-Agreement and WSLA, they are not intended to express SLA. The basic assumption here is that the services themselves do not represent any value to the business; their use is valuable. Defining services as close as possible to the place of use - by consumers - we strive to create business benefits in a minimal, “just in time” manner.
Finally, we have to point out the risk: by allowing customer contracts to manage the specification of a service provider, we can undermine the conceptual integrity of the service. Services contain discrete, identifiable, reusable functions whose integrity should not be compromised by unreasonable requirements that go beyond their intended use.
- Ian Robinson. Consumer-Driven Contracts (original article)
- Martin Fowler. Microservices , Decentralized Governance section
- Ham Focke. The pyramid of tests in practice , the section "Contract tests"
- Himion Dmitry. The core of test automation in microservice architecture