Skip to content

Jamie-Rodriguez/hex-contracts-demo

Repository files navigation

This repo demonstrates an example of using the hexagonal/ports and adaptors/onion architecture and the advantages it provides for making testing easier. In particular this focuses on an example of using contract testing to verify that in-memory implementations (sometimes referred to as "fakes") have the equivalent behaviour of the external services; which can then be used to make unit testing easier.

Overview of the Domain

Our service retrieves the current weather data from Weather Station, it then adds a comment based on the current weather data, which it then sends to a Weather Reporter service.

flowchart LR
    WeatherStation["Weather Station"] --> OurService("Our Service")
    OurService --> WeatherReporter["Weather Reporter"]
Loading

Overview of Architecture

We can create equivalent in-memory implementations of the external services for use in unit tests. Contract tests are created to verify that both the in-memory and live service implementations have the same behaviour i.e. they conform to the same contract.

graph LR
    subgraph WeatherStation["Weather Station"]
        InMemoryWeatherStation["In-Memory"]
        RemoteWeatherStation["Remote Service"]
    end

    subgraph OurService["Our Service"]
        WeatherStationForOurService["Weather Station"]
        WeatherReporterForOurService["Weather Reporter"]
    end

    subgraph WeatherReporter["Weather Reporter"]
        InMemoryWeatherReporter["In-Memory"]
        RemoteWeatherReporter["Remote Service"]
    end

    InMemoryWeatherStation & RemoteWeatherStation -.-> ContractTestingForWeatherStation("Contract Testing")
    InMemoryWeatherReporter & RemoteWeatherReporter -.-> ContractTestingForWeatherReporter("Contract Testing")

    WeatherStation ~~~ OurService
    OurService ~~~ WeatherReporter

    WeatherStation --> WeatherStationForOurService
    WeatherReporter --> WeatherReporterForOurService
Loading

I offer a hand-drawn diagram as the Mermaid rendering engine currently isn't rendering a sensible diagram:

Hand-drawn architecture diagram

Now that we've proven that both implementations obey the same contract. We can then use the in-memory implementations in unit-testing!

Ports and Adapters (Hexagonal) Architecture

Hexagonal architecture diagram

From What's Hexagonal Architecture? by Luis Soares

Running the demo

In this repo, we emulate remote services via Docker and Docker Compose. In real life, these would be remote services that your service interacts with, but only exist remotely i.e. cannot be spun up locally for local development.

Navigate to our service at our-service/.

cd our-service

Next start up the remote services:

npm run start-services

Finally we can start up our service. Note that you need to provide the base URLs of the remote services via the following environment variables:

  • REMOTE_WEATHER_STATION_URL
  • REMOTE_WEATHER_REPORTER_URL

The default values are provided below (see containerization/docker-compose.yaml for further configuration)

REMOTE_WEATHER_STATION_URL=http://localhost:80 REMOTE_WEATHER_REPORTER_URL=http://localhost:82 node index.js

Teardown

The external services can be stopped with

npm run stop-services

Testing

To run unit tests:

npm run test:unit

And likewise, for contract tests:

npm run test:contract

Further Reading

About

Example repo demonstrating the hexagonal/ports-and-adapters architecture and contract testing

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published