Hexagonal architecture

Hexagonal architecture

Let’s have a look at how BikeShare is organized. It follows a hexagonal architecture (also called ports and adapters), which splits responsibilities as follows:

  • The controller is responsible for dealing with HTTP and JSON.

  • The application layer orchestrates the domain, through use cases (or services).

  • The domain contains the business objects (Bike, Rental, Station), and their behaviour.

  • The repository is responsible for accessing the data. For now it only contains fake, in-memory data, because we have not seen data access yet.

The key concept is separation of concerns: each class is responsible for a single, specific responsibility.

Ports and adapters

Hexagonal architecture is built around two ideas:

  • A port is an interface describing what the application core needs, without saying how it is fulfilled. In BikeShare, BikeSharingRepository is a port: it declares operations like finding or saving a Bike, but says nothing about where the data actually lives.

  • An adapter is a concrete implementation plugged into a port. InMemoryBikeSharingRepository is an adapter: it implements BikeSharingRepository using a simple in-memory fake. Later in the course, we will plug in a different adapter backed by a real database, without changing a single line of the application or domain layer.

Ports are owned by the application layer, next to the use cases that need them. The domain layer stays oblivious to ports and adapters entirely: it only contains business objects and rules.

Tip
You may also encounter the vocabulary of primary (or driving) adapters, like the controller, which drives the application, and secondary (or driven) adapters, like InMemoryBikeSharingRepository, which the application drives in return.
bikeshare hexagonal architecture

Architecture tests with ArchUnit

BikeShare comes with a very readable architecture test, written with ArchUnit. Its most important rule is that the domain must not depend on any framework or infrastructure type at all: no Spring annotation, no JPA, no HTTP type should ever appear in a domain class. This is what keeps the domain portable, easy to understand, and usable outside of Spring Boot entirely.

Integration testing with MockMvc

Thanks to this separation, the application and domain layers can be tested with plain, fast unit tests, without starting Spring at all.

BikeShare also includes an integration-level automated test based on MockMvc. It exercises the full stack, from the HTTP layer down to the domain, without starting a real server. This complements the unit tests by checking that all the pieces are correctly wired together.