Overview

5 Crossing the boundaries: Adapters, repositories, and the mapping problem

The chapter explains how the application core, which already contains well-defined use cases, commands, queries, and handlers, connects cleanly to the outside world. It introduces the outer ring of the hexagon: input adapters that receive external requests and turn them into bus-friendly commands or queries, and output adapters that implement the core’s ports while hiding frameworks and delivery mechanisms. The main idea is that adapters are translators, not places for business logic, and that the core should remain isolated even as the system becomes usable through HTTP, messaging, or other interfaces.

A major focus is how to keep boundaries explicit when data crosses them. On the input side, adapters map protocol-specific requests into application inputs and convert typed application errors into protocol-specific responses, such as HTTP problem details. On the output side, adapters implement role interfaces shaped by the core’s needs rather than by infrastructure APIs, and they translate between domain language and technical concerns. The chapter stresses that DTOs should usually be the currency at boundaries, preventing domain entities from leaking into controllers, APIs, or external service calls.

Repositories get special treatment because they are output ports that legitimately work with domain entities: they load, persist, and reconstitute aggregates so handlers can invoke domain behavior on real objects. The chapter recommends separating read-side and write-side repository methods, keeping persistence frameworks like Spring Data hidden inside adapters, and using explicit mappers to move between domain entities, persistence models, DTOs, requests, and responses. It then compares four mapping strategies, from no mapping at all to the fully isolated “Full Bridge,” concluding that more mapping usually buys better decoupling, with the most isolated approach best suited to larger, longer-lived, or public systems.

This chapter completes the outside ring of the hexagon. Input adapters translate external requests into commands and queries, output adapters implement the ports required by the core, and explicit mapping keeps each boundary separate.
Two input adapters, one use case. The REST controller and the message consumer both call the same command bus. The handler behind the bus is identical regardless of which adapter triggered it.
Error hierarchy to HTTP status code mapping. Each sealed subtype maps to a specific status code. The compiler enforces completeness—no subtype can be silently ignored.
The header interface on the left is shaped by the email implementation. The role interface on the right is shaped by what the application core needs, keeping the port technology-agnostic.
Spring Data reinterpretation. The domain-owned ArticleRepository at the top (annotated with the Shared Kernel's @Repository) defines the contract. The adapter in the middle implements that port and delegates to Spring Data at the bottom (annotated with Spring's @Repository). Two different annotations with the same name, two different layers, two different purposes.
The Full Bridge for a publication-readiness query. The request enters as raw HTTP input, becomes an application query, resolves to a validated domain identifier, loads the Article aggregate because the answer depends on domain rules, maps the domain result to a DTO, and finally maps that DTO to an HTTP response. No type crosses more than one boundary.

Summary

  • Input adapters are translators that receive external stimuli—HTTP requests, messages, scheduled triggers—and convert them into commands or queries the bus understands. They map the Error hierarchy to protocol-specific responses using exhaustive switch on sealed types. Framework annotations belong here, not in the core.
  • Output adapters implement port interfaces defined by the core, using whatever technology is appropriate. They translate between the port's domain-language DTOs and the infrastructure's native format. The core never sees the adapter—only the port.
  • Role interfaces vs header interfaces determine whether your ports protect the core or leak the infrastructure. A role interface is shaped by what the consumer needs. A header interface mirrors the implementation. Always design ports from the inside out.
  • DTOs are the currency of output port boundaries—lightweight, immutable records that carry exactly the data the port needs. Domain entities never cross output port boundaries directly. The one exception is repositories.
  • Repositories are special output ports that work directly with domain entities. Their purpose—persisting and reconstituting domain state—requires the domain model to cross the boundary. The repository interface lives in the core, annotated with the Shared Kernel's @Repository. The implementation lives in the adapter layer, annotated with @Adapter.
  • Repository interfaces separate read-side from write-side through naming conventions: save and delete for writes, read for loading domain entities into command handlers, and find for returning DTOs to query handlers. Granularity beyond this—slicing into multiple interfaces—is a team decision, not an architectural one.
  • Spring Data belongs inside the adapter, never on domain-owned ports. The SpringArticleRepository extends ListCrudRepository operating on a persistence entity, hidden behind the JpaArticleRepository adapter that the core depends on.
  • Two @Repository annotations with the same name, two different layers, two different purposes.
  • In WriteFlow, these boundaries complete the outside ring of the hexagon: REST controllers drive article use cases, repository adapters persist and reload articles, notification adapters deliver publication-related messages, and mappers keep each external representation separate from the domain model.
  • The mapping problem arises at every boundary crossing. Four strategies exist along the convenience-isolation spectrum: no mapping (anti-pattern), one-way (API decoupled), two-way (API and persistence decoupled), and the Full Bridge (complete isolation at every boundary). Two-way is the pragmatic default. The Full Bridge is the right choice for public APIs, large teams, and long-lived systems. The only incorrect choice is no mapping at all.

FAQ

What is the main purpose of input adapters in hexagonal architecture?They sit on the left side of the hexagon, receive external requests in protocol-specific formats, translate them into commands or queries the bus understands, and translate the result back into the delivery format expected by the client or trigger.
Do input adapters contain business logic?No. Input adapters are translators only. They should not decide business rules, inspect database state, or coordinate use cases. That work belongs to handlers and the application core.
How do input adapters handle errors in a REST API?They map application-level Error values to protocol-specific HTTP responses. For example, validation errors may become 400 Bad Request, missing resources 404 Not Found, and conflicts 409 Conflict.
Why does the book emphasize sealed Error hierarchies and exhaustive switches?Because sealed types let the compiler enforce complete error mapping. When a new error subtype is added, every switch over the hierarchy must be updated, preventing forgotten or unsupported error responses.
What is the role of output adapters?Output adapters implement the ports defined by the application core and translate between the core’s language and infrastructure-specific technologies such as email services, HTTP clients, databases, or message systems.
What is the difference between a role interface and a header interface?A role interface is shaped by what the core needs from an external collaborator. A header interface is shaped by the implementation. In hexagonal architecture, ports should be role interfaces so they protect the core from infrastructure details.
Why are repositories treated differently from other output ports?Repositories are privileged output ports because they must work directly with domain entities to persist and reconstitute domain state. Unlike most output ports, they intentionally allow domain objects to cross the boundary.
Why should repository interfaces be split into read-side and write-side methods?Because different consumers need different things. Write-side methods like save and delete support command handlers, while read-side methods like findById or findByAuthor support query handlers. This follows the Interface Segregation Principle.
Why should Spring Data not be exposed directly through the domain port?Because that would turn the port into a header interface and couple the core to Spring Data’s methods and persistence annotations. The framework should stay inside the adapter, while the domain owns the repository contract.
Which mapping strategy is recommended for most applications?The book recommends two-way mapping as the pragmatic default: the domain is separate from persistence, DTOs are used for data leaving the application, and mapping is done at the boundaries. The Full Bridge is reserved for larger, longer-lived, or public systems.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Clean Applications with Hexagonal Architecture ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Clean Applications with Hexagonal Architecture ebook for free