Skip to content

Hexagonal Architecture with Java Spring

Hexagonal architecture, also called ports and adapters, was described by Alistair Cockburn in 2005. Its stated goal is to build an application that runs without either a user interface or a database, so that it can be exercised by automated regression tests, keep working when the database is unavailable, and be linked to other applications without a person in the loop.

The consequence for design is that a system’s interfaces are designed by purpose rather than by technology. The application declares what it needs — “store an article”, “notify an author” — and technologies are substituted behind those declarations as adapters.

Cockburn draws the application as a hexagon: the application sits in the middle, and each facet is one purpose-shaped interface. Around each facet sit interchangeable adapters for that purpose — an HTTP adapter and a test adapter on the driving side, a real database adapter and an in-memory mock on the driven side. The number of sides carries no meaning; the hexagon is simply a shape with room to draw several distinct facets.

The pattern also distinguishes primary actors from secondary actors. A primary actor drives the application — a person using a UI, or an automated test suite. A secondary actor is driven by the application, either to answer a question or to be notified: a database, a message broker, an email gateway.

  • The domain is the core of the hexagon. It holds the business logic and is free of infrastructure and framework boilerplate.
  • Ports are plain Java interfaces through which adapters plug into the domain — repository interfaces, publisher interfaces, and the use-case interfaces the domain exposes outward.
  • Adapters are either the external API of the application or clients of other systems. They translate the interfaces of external systems into the interfaces the domain exposes through its ports.

Every dependency points inward. The domain never imports an adapter; an adapter imports the port it implements.

A literal transcription of the pattern gives:

com.puglieseweb.app.sampleapp
config
adapters
broker
db
repositories

Mapped onto Spring naming conventions, the same structure reads:

adapters
inbound
outbound
domain
service (the ports)
model
config

adapters/inbound holds the driving side — controllers, listeners, scheduled entry points. adapters/outbound holds the driven side — repositories, broker publishers, HTTP clients. domain/service holds the ports and the use cases that sit behind them.

Each adapter works on its own data model and is responsible for translating that model to and from the domain. The domain model itself stays adapter-agnostic: it must not know that a REST response or a database row exists.

class ArticleResponse {
private final String id;
private final String title;
private final String content;
private final String authorName;
static ArticleResponse of(Article article) {
return new ArticleResponse(article.id().value(),
article.title().value(),
article.content().value(),
article.author().name());
}
// boilerplate omitted
}

That is why the conversion belongs on the adapter’s type:

ArticleResponse.of(domainArticle)

and not on the domain type:

domainArticle.toResponse()

The second form would put knowledge of the REST adapter inside the domain, and the domain would then have to change every time a new adapter arrived.