preface
Some time ago, I talked about contract with Chen Xin from our company on the subway. He described a project he had done before, which provided a mechanism to promote the cooperation between the front and back end in the development process by contract first. This development method has the following advantages:
- After this contract is defined, front-end students can develop and coordinate without relying on the back-end interface, greatly reducing the waiting time for the back-end interface.
- As long as the front-end development is completed, the test can conduct rapid verification of the function in the advanced field, and put forward some optimization suggestions from the testing perspective.
- A quick Demo can be provided to demonstrate business value with the customer.
Immediately, I became very interested in the contract preemption model. After reading about it, an interesting concept came to me: Consumer-driven Contracts, which I will replace with CDC.
Consumer – Driven Contracts origin
On 25 April 2006, Ian Robinson spoke with Martin Fowler and Ron Jacobs on “Evolution of Architecture” at the Microsoft Architect Insight Conference Architecture) “The concept of CDC (Consumer Driven Contracts) is presented in the discussion. In June of that year, Ian Robinson’s article Consumer Driven Contracts: A Service Evolution Pattern was published on Ma’s personal website.
Ian Robinson describes CDC as a service evolution model and gives Consumer Driven Contracts: A Service Evolution Pattern summarizes the similarities and differences between Provider Contracts and Consumer Contracts and consumer-driven Contracts.
In this paper, Ian Robinson makes a basic assumption that the Provider itself has no value to the business, and its value lies in being consumed. Therefore, Ian Robinson believes that the expression of business value should be left to services (consumers) that are closer to the user. Designed to develop business value in a lean, timely manner. Under this assumption, the CDC was born.
CDC was born in the context of service-oriented Architecture (SOA) and distributed architecture, mainly to address the requirements of architecture evolution. CDC has established a rapid feedback mechanism between consumers and providers to facilitate collaboration to ensure that the evolution of providers does not affect consumers. CDC could conceivably apply to microservices as well.
The popularity of microservices
The domain-Driven Design (DDD) and microservices architectural styles are in full force today. Microservices are an architectural and organizational approach to developing software that consists of small independent services that communicate through well-defined apis. These services are managed by small independent teams. Microservices architectures make applications easier to scale and faster to develop, accelerating innovation and shortening time-to-market for new features.
All fine words, of course, but microservices are governed by Conway’s Law, cross-departmental KPIs, and the basic skills of team members.
However, in the complex relationship between microservices, very high demands are placed on the evolution of services and testing between services.
Testing issues with microservices
Assume that our system contains multiple microservices, and the dependencies are shown as follows:
Now we want to test one of these services to verify that it can communicate properly with other services, and we have two options
- Deploy all microservices for end-to-end testing
- Mock external services in unit tests and integration tests
Both have their advantages and disadvantages
Deploy all microservices for end-to-end testing
Advantage:
- Simulated production environment
- Test real communication between services
Disadvantage:
- Testing is expensive, and testing one of the services requires simultaneous deployment of all the microservices involved in the dependency chain
- The failure of one of these services will block the end-to-end testing
- Slow runs, slow tests, and a chain reaction of very slow feedback
Mock external services in unit tests and integration tests
Advantage:
- Feedback quickly
- Infrastructure requirements are low
Disadvantage:
- The Mock out external interface does not represent the real implementation of the service provider, and there is information asymmetry on both sides
- Passing the test doesn’t mean it’s okay to go live
Difficulties in microservice architecture evolution
Photo credit: Abelsu7.top /2019/09/18/…
To give the reader a sense of substitution, I propose two hypotheses here:
-
The diagram above shows our microservice network topology, showing the dependencies between services.
-
We have done automated unit testing, automated in-application integration testing, and built a sound CI/CD pipeline.
However, with this suffocating dependency, even with good quality assurance at the development level (ensuring only reliability within a single service), there are two thorny issues with service evolution and interaction
- How can services evolve independently of each other (services evolve at different rates, and we don’t want to be forced to upgrade all downstream services in order to upgrade one service)?
- How to ensure that the service evolution does not affect downstream consumers once it goes live?
How does CDC safeguard architectural evolution
Related terms:
- Provider – The service Provider
- Consumer — people who consume services
- Contract, Contract
- Expectation, expect
From the figure above, the following conclusions can be drawn
- Consumers and providers communicate and collaborate around key business values.
- The Consumer will import the Expectation into the Contract.
- After the Provider receives the Contract, it exports the business elements required by the Consumer according to the Expectation.
- Both Consumer and Provider fulfill their commitment to Contact through automated contract testing.
The first three steps focus on the definition and implementation of Contract. In the fourth step, the original oral Contract is firmly bound to the life cycle of Provider and Consumer through automated Contract testing.
An interesting situation arises here, where the evolution of providers is strongly dependent on the stability of Consumer business requirements
The stability of the Contract
In this case, the Provider can choose not to evolve or to evolve (internal reconstruction). As long as the Consumer and Provider use the same Contract, the Provider can pass the Contract test. We have reason to believe that the Provider can meet the business needs of consumers, and we have the confidence to evolve and go live independently for the Provider.
If the Provider causes a contract test failure during the evolution process (the rapid feedback mechanism provided by the contract test), the Provider can independently evolve and go online as long as it fixes the failed contract test.
Unstable Contract
Because business requirements change and Contract changes (new or modified), in this case the Provider must evolve with the Consumer, that is, the service evolves only where the Consumer expresses explicit requirements.
If multiple contracts apply to the same Provider interface, we can use the principle that “the sending behavior of an implementation must be conservative and the receiving behavior must be free.” We can follow this principle in service evolution, such that the Provider interface returns business elements that cover multiple Contracts, and the Consumer (receiver) only chooses to assert “just enough” for the elements in the Contract. This principle is important for Provider services, because only in this way can the Provider interface be reused and not be trapped in version hell.
CDC principles for solving microservice testing problems
The implementation principle of consumer-driven contract test is basically the same, and the implementation process can be roughly divided into three steps
- Add a Mock Server (Mock Provider in the figure) whose primary responsibility is to provide access to consumers as described in the Contract.
- The Consumer makes a request to the Mock Provider and either writes the request result into the Contract or matches the request result to the Expectation of the Contract (depending on how the Contract is generated).
- Replay the Consumer’s request back to the Provider and match the Consumer’s and Provider’s request results.
Through the above three steps, you can know whether the Consumer and Provider have fulfilled their commitment to the contract.
Here are three visualizations of the process:
This solution solves the drawback of “start all microservices” and simulates more realistic service communication, thus killing two birds with one stone.
Test framework for Consumer-driven Contracts
- Spring Cloud Contract
- Pact
The challenge of Consumer Driven Contracts
However, there are some challenges to this seemingly good model
- If a single Provider fails to meet the Expectation of consumers, it may prompt the Provider to take another path, which will destroy the integrity of the concept of the Provider. In this case, BFF (Backend For Frontend) may be required.
- Infrastructure inevitably introduces a layer of complexity and protocol dependencies.
- You need to train developers with the right skills.
- How testing strategies are compatible with a combination of testing practices.
conclusion
Consumer-driven Contracts are a pattern of architectural evolution that occurs only where consumers express explicit requirements; At the development level, it promotes the contract prior principle and guarantees the reliability of services by simulating the communication between services through contract testing. At the delivery level, it encourages Consumer and Provider collaboration around key business values, starting with minimal lean requirements, eliminating waste, not over-implementation, and focusing on the delivery of functionality.
In the era of agile and microservices, consumer-driven Contracts will be a useful sword.
subsequent
Next we’ll take a look at how Spring Cloud Contract implements consumer-driven Contracts.