Instead of allowing the application core to depend on infrastructure, Hexagonal Architecture ensures that the business logic remains independent of frameworks and technologies.
External systems interact with the application through well-defined ports, while adapters translate between the application's domain model and external technologies.
This approach improves maintainability, testability, and flexibility, making it popular in modern Spring Boot applications and microservices.
Why Do We Need Hexagonal Architecture?
In many traditional applications, the business logic is tightly coupled to framework components such as controllers, repositories, databases, and external services. Controller
|
v
+----------------+
| Service |
+----------------+
|
v
+----------------+
| Repository |
+----------------+
|
v
+----------------+
| Database |
+----------------+
As the application grows, replacing the database, integrating a message broker, exposing a new API, or changing frameworks often requires modifications to the business layer.
Hexagonal Architecture addresses this problem by placing the business logic at the center of the application.
Instead of depending on infrastructure, the application communicates with external systems through ports, while adapters handle the technology-specific implementations.
Hexagonal Architecture
Hexagonal Architecture consists of three primary building blocks: the Application Core, Ports, and Adapters. The following diagram illustrates how these components interact.
Instead, external systems interact with the application through Ports, while Adapters provide the technology-specific implementations.
Typical Project Structure
A typical Spring Boot project following Hexagonal Architecture organizes the application into following package strcuture.com.example.order
βββ domain
β βββ model
β β βββ Order.java
β βββ ports
β β βββ inbound
β β β βββ PlaceOrderUseCase.java
β β βββ outbound
β β βββ OrderRepository.java
β βββ service
β βββ OrderServiceImpl.java
β
βββ adapters
β βββ inbound
β β βββ rest
β β βββ OrderController.java
β βββ outbound
β βββ persistence
β βββ OrderEntity.java
β βββ OrderMapper.java
β βββ JpaOrderRepository.java
β βββ SpringDataOrderRepository.java
β
βββ configuration
The domain package contains business entities, domain services, and port interfaces.
Since it has no dependencies on Spring Boot, databases, or messaging libraries, the business logic can be tested independently.
The inbound adapters expose the application through technologies such as REST controllers, GraphQL APIs, or message consumers.
They receive requests from external clients and invoke the appropriate domain services through inbound ports.
The outbound adapters implement outbound ports to communicate with external systems such as databases, Kafka, RabbitMQ, REST APIs, or file systems.
If the underlying technology changes, only the adapter implementation needs to be replaced while the domain layer remains unchanged.
The configuration package wires together the domain, ports, and adapters using Spring Boot dependency injection, allowing the application core to remain completely independent of infrastructure concerns.
Application Core (Domain)
The Application Core is the heart of a Hexagonal Architecture.It contains the application's domain models, business rules, and use cases, while remaining completely independent of frameworks and infrastructure.
The domain model represents the core business concepts, while use cases implement the application's business operations.
Together, they define the behavior of the application without depending on Spring Boot, JPA, databases, REST controllers, or messaging systems.
The Application Core typically contains the domain model, use case implementations, and port interfaces.
public class Order {
private Long id;
private Customer customer;
private List items;
private OrderStatus status;
}
Business operations are exposed through inbound ports.
public interface PlaceOrderUseCase {
void placeOrder(CreateOrderRequest request);
}
The application core implements these use cases by validating requests, enforcing business rules, calculating totals, reserving inventory, and coordinating other business operations without depending on infrastructure-specific code.
Ports
Ports define how the Application Core communicates with the outside world.They are simple interfaces that describe either the business capabilities offered by the application or the external services required by it.
Hexagonal Architecture defines two types of ports.
Inbound Ports
An Inbound Port represents a business use case that external clients can invoke.REST controllers, GraphQL APIs, CLI commands, and message consumers call these interfaces without knowing how the business logic is implemented.
public interface PlaceOrderUseCase {
void placeOrder(CreateOrderRequest request);
}
Outbound Ports
An Outbound Port represents an external dependency required by the application core, such as a database, payment gateway, message broker, or another service.public interface OrderRepository {
void save(Order order);
Optional findById(Long id);
}
The application core depends only on these interfaces, allowing infrastructure implementations to change without affecting the business logic.
Adapters
Adapters provide the technology-specific implementations of ports.They translate between the application's domain model and external technologies such as REST APIs, databases, Kafka, RabbitMQ, or third-party services.
Inbound Adapters
An Inbound Adapter receives requests from external clients and invokes an Inbound Port. A Spring Boot REST controller is a common example.@RestController
@RequestMapping("/orders")
public class OrderController {
private final PlaceOrderUseCase useCase;
@PostMapping
public void createOrder(@RequestBody CreateOrderRequest request) {
useCase.placeOrder(request);
}
}
The controller handles HTTP-specific concerns and delegates the business operation to the application core.
Outbound Adapters
An Outbound Adapter implements an Outbound Port and communicates with external systems. For example, a Spring Data JPA adapter implements the OrderRepository interface.@Repository
public class JpaOrderRepository implements OrderRepository {
private final SpringDataOrderRepository repository;
@Override
public void save(Order order) {
OrderEntity entity = OrderMapper.toEntity(order);
repository.save(entity);
}
@Override
public Optional findById(Long id) {
return repository.findById(id).map(OrderMapper::toDomain);
}
}
The adapter is responsible for translating between the application's domain model and the JPA entity.
This keeps framework-specific classes such as @Entity outside the Application Core, allowing the business logic to remain independent of the persistence technology.
Note: In strict Hexagonal Architecture, the domain model remains free of framework annotations such as @Entity. JPA entities belong to the persistence adapter, which maps them to and from the domain model.
Many Spring Boot applications simplify this by using the same class as both the domain model and JPA entity, although this introduces a dependency on the persistence framework.
Request Flow
The following example shows how a typical HTTP request flows through a Spring Boot application built using Hexagonal Architecture.A REST controller receives the request and invokes an Inbound Port. The application core executes the business logic and, when persistence is required, communicates through an Outbound Port.
Finally, the Outbound Adapter invokes the underlying persistence technology. Throughout the request, the Application Core remains independent of Spring Boot and the database implementation.

Replacing Infrastructure
One of the biggest advantages of Hexagonal Architecture is that infrastructure can be replaced without changing the Application Core.Suppose an application initially stores orders in PostgreSQL. The application core communicates through the OrderRepository port, while a JPA adapter provides the implementation.
Application Core
|
v
+------------------+
| OrderRepository |
+------------------+
|
v
+------------------+
|JpaOrderRepository|
+------------------+
|
v
PostgreSQL
Later, the organization decides to migrate from PostgreSQL to MongoDB. Instead of modifying the business logic, only the outbound adapter is replaced.
Application Core
|
v
+-------------------------+
| OrderRepository |
+-------------------------+
|
v
+-------------------------+
| MongoDbOrderRepository |
+-------------------------+
|
v
MongoDB
The Application Core continues to depend only on the OrderRepository interface.
Since the business logic has no knowledge of the underlying database, replacing PostgreSQL with MongoDB requires changing only the adapter implementation while the domain model and business rules remain unchanged.
Testing
One of the biggest advantages of Hexagonal Architecture is that the Application Core can be tested independently of databases, REST APIs, message brokers, and other infrastructure.Because the business logic depends only on ports, outbound adapters can easily be replaced with test implementations or mock objects.
OrderRepository repository =
Mockito.mock(OrderRepository.class);
PlaceOrderUseCase useCase =
new OrderServiceImpl(repository);
useCase.placeOrder(request);
verify(repository).save(any(Order.class));
The test invokes the application core directly while the mocked OrderRepository simulates database interactions.
As a result, business rules can be verified without requiring a running database, Spring Boot application, or Spring container, making unit tests fast, reliable, and easy to maintain.
When Should You Use Hexagonal Architecture?
Hexagonal Architecture is most beneficial for applications with complex business logic that must remain independent of frameworks and infrastructure.It is particularly well suited for Spring Boot microservices, enterprise applications, and domain-driven systems that integrate with multiple databases, external APIs, message brokers, or user interfaces.
By isolating the application core behind ports, the pattern improves maintainability, testability, and flexibility.
Infrastructure technologies such as databases, messaging systems, and external services can be replaced by changing adapters while leaving the business logic unchanged.
When Should It Be Avoided?
Hexagonal Architecture introduces additional interfaces, adapters, and abstractions, making the project structure more complex than a traditional layered application.For small applications with simple CRUD operations and minimal business logic, this additional abstraction often provides little benefit while increasing development and maintenance effort.
Unless an application is expected to evolve, integrate with multiple external systems, or require a clear separation between business logic and infrastructure, a traditional layered architecture is usually simpler and sufficient.
Summary
Hexagonal Architecture separates business logic from infrastructure by introducing ports and adapters.The application core depends only on interfaces, while technology-specific implementations remain outside the core.
This separation improves maintainability, flexibility, and testability by allowing databases, messaging systems, and external APIs to be replaced without changing business logic.
Although Hexagonal Architecture introduces additional abstractions, it enables applications to evolve independently of frameworks and infrastructure.