Skip to content

spring-cloud-samples/the-legacy-app

 
 

The Legacy App

This legacy app is a stereotypical monolith, with legacy code that nobody loves. It has an anemic structure. We’ve left just one domain which is the CustomerRentalHistory. It will call this API https://stripe.com/docs/api/java#list_charges

The script

The Legacy App

The Legacy App Introduction

Set up a scenario. We have a problem with a legacy service. Let’s see how Spring Cloud Contract can solve this. The legacy app is the stripe.com API, fronting a mainframe, it offers many API’s one of which is the CustomerRentalHistory API returns JSON response containing lots of fields about the customer including their name / address / and how many rentals they have made in the past. For simplicity sake we’re returning last 25 charges.

A explains the legacy API (https://stripe.com/docs/api/java#list_charges) to M and talks through the kind of problems he has seen with the API & which fields he only cares about. Amount, Captured, Customer A provides a tour of the client side code which currently exists to call the CustomerRentalHistory service.

The Legacy App Contract

M steps in takes over the keyboard and show A how to write a contract test to generate a stub for the rental service A asks questions to clarify and reinforce what M is doing.

  • We don’t want to fake the response manually cause we want to verify the integrations

    • how can we do that?

    • who knows WireMock?

    • what is a stub?

  • In stubs

    • we add spring-cloud-contract-maven-plugin section

    • we add the <skipTests>true</skipTests> cause we don’t want to run tests

    • we add the starter-verifier dependency

    • we write the first contract that returns the contents of the charges.json

    • when we do ./mvnw clean install -pl com.example:the-legacy-app-stubs we will see that stubs got created

  • In impl

    • we write a test that asserts that the response contains 25 results

    • we run it - will it pass? Of course not…​

    • we add spring-cloud-starter-contract-stubrunner

    • we write a test that uses @AutoConfigureStubRunner and works offline to download stubs

    • we assert that the response contains 25 results

Generating tests against the real API (via DSL)

hint, pick this or explain proxying via WireMock

A asks a question, what good is my stub if it wasn’t tested against the real API.

Ok, so, from the contracts let’s generate the tests to see if the result is valid.

Let’s write some code!

  • Let’s define in the plugin the EXPLICIT mode of tests. We want to send a real request

  • we need to change the contract cause Stripe generates the response data. Our json will not pass these validations. That’s why in Sc-Contract we have the dynamic and static bits. For the test we will call an assertion function and for the stub side we will return a fixed value from the file

    • the value from the file can be updated just before the test

    • the value can be taken not from the file but from the external API

    • there are different ways, but for simplicity’s sake we’ll take it from the file

  • let’s create a base class

    • we create the method assertResponse(…​)

    • we set up the URI for the call

    • we set up basic authentication

Now we can execute the build (debug from IDE) and we’ll see that we’ve managed to connect to Stripe and assert the response.

Generating tests against the real API (via WireMock)

hint, pick this or explain proxying via DSL

How can we generate stubs in the easiest way? So that they are validated and reusable? We can create a proxy via which we will send requests to the real app! Then from this proxy we will store all the stubs in our output directory.

Thanks to the Maven assembly plugin, we can then package the stubs in a JAR in the structure that is understood by the Stub Runner. That way the captured responses from the real API can be reused offline in your company!

The Legacy Summary

At the end of the session

  • A’s code base has an extra contract test in it

  • The maven plugin has been added to the project

  • The contract is in the client side code base

    • we create a submodule with src/test/resources/contracts

    • setup maven plugin to first generate tests (which will fail)

    • then we set the maven plugin NOT to generate tests, only generate stubs because we don’t own the production code those stubs will be reused in the stub runner tests

  • A test with StubRunner has been written and executed

  • The producer side test has not been executed at this point

External contracts

External contracts introduction

A then asks where the producer test should live. A says he is good friends with the developers of the bad API and A builds a pull request for A’s git repo which has the contracts in it and demonstrates how A can integrate the app into the tests. Explain that ones the contracts are validated against the producer then we’re talking about producer contracts.

M says how to move the contracts to an external repository it happens that we already have one prepared here:

$ git clone https://github.com/marcingrzejszczak/2017-s1p-external-contracts.git

M explains the structure:

  • contracts lay under main cause we need to build the JAR with contracts

  • explain the assembly plugin

  • each application has its own slash separated groupid/artifactid folder structure

    • under that folder it’s good practice for each consumer to their own subfolder

    • we don’t want to run tests here (that’s why we skip them)

External contracts coding

Time for action:

  • we create the missing src/main/resources/com/example/customer-rental-history folder

  • we set up the pom.xml for customer-rental-history contracts (copy it from done folder)

  • we do mvn clean install to install the stubs locally and update the tests in the IDE to point to these stubs

  • we’ve proven that we can run tests locally against the stubs from the contract

  • we cd back to root and do ./mvnw clean install - we show the JAR with all contracts

External contracts summary

At the end of the session

  • A separate, single repository with all the contracts got created

  • A’s code base no longer uses the contracts from the stub module cause the stubs come from the external repo

    • That will work only under the assumption that the CustomerRentalHistory team will start doing contract testing

  • Now, all teams can store contracts in that repository and collaborate

  • We’re ready to make the legacy application test its own API

Contracts for legacy app

Contracts for legacy app introduction

A realizes that he is just as bad as CustomerRentalHistory with his clients and now A wants to adopt SCC for his FraudDetectionService.

  • we setup SCC for FDS

    • to simplify the process we will store the contracts with the app

  • copy the contracts for the legacy app with stubsPerConsumer structure

    • let’s assume that there are 2 clients audit-service and insurance-service

    • audit needs the message field

    • insurance needs the message & ex27 fields

M explains the idea

A asks, can I delete ex27 field? I don’t remember if anybody uses it…​ A explains what consumer contracts are.

Continuous Delivery

What does this actually mean in the context of CD (SLIDES TIME) - Can you safely delete a field in your API? - Have my consumers changed and break me? - Are my API changes backward compatible?

Microservices (slides)

  • Recapping what we’ve learned

  • Consumer Driven Contracts (A’s previous presentation)

  • NodeJS

Releases

No releases published

Packages

No packages published

Languages

  • Java 76.4%
  • Shell 12.4%
  • Groovy 11.2%