Hexagonal Architecture in Java
Last Updated :
25 Apr, 2025
As per the software development design principle, the software which requires the minimum effort of maintenance is considered as good design. That is, maintenance should be the key point which an architect must consider. In this article, one such architecture, known as Hexagonal Architecture which makes the software easy to maintain, manage, test, and scale is discussed.
Hexagonal architecture is a term coined by Alistair Cockburn in 2006. The other name of Hexagonal architecture is Ports And Adapters architecture. This architecture divides an application into two parts namely, the inside part and the outside part. The core logic of an application is considered as the inside part. The database, UI, and messaging queues could be the outside part. In doing so, the core application logic has been isolated completely from the outside world. Now the communication between these two parts can happen through Port and Adapters. Now, let’s understand what each of these means.
- The Ports: The Ports acts as a gateway through which communication takes place as an inbound or outbound port. An Inbound port is something like a service interface that exposes the core logic to the outside world. An outbound port is something like a repository interface that facilitates communication from application to persistence system.
- The adapters: The adapters act as an implementation of a port that handles user input and translate it into the language-specific call. It basically encapsulates the logic to interact with outer systems such as message queues, databases, etc. It also transforms the communication between external objects and core. The adaptors are again of two types.
- Primary Adapters: It drives the application using the inbound port of an application and also called as Driving adapters. Examples of primary adapters could be WebViews or Rest Controllers.
- Secondary Adapters: This is an implementation of an outbound port that is driven by the application and also called as Driven adaptors. Connection with messaging queues, databases, and external API calls are some of the examples of Secondary adapters.
Therefore, the hexagonal architecture talks about exposing multiple endpoints in an application for communication purposes. If we have the right adapter for our port, our request will get entertained. This architecture is a layered architecture and mainly consists of three layers, Framework, Application, and Domain.
- Domain: It is a core business logic layer and the implementation details of the outer layers are hidden with this.
- Application: It acts as a mediator between the Domain layer and the Framework layer.
- Framework: This layer has all the implementation details that how a domain layer will interact with the external world.
Illustrative Example: Let’s understand this architecture with a real-time example. We will be designing a Cake Service application using Spring Boot. You can create a normal Spring or Maven-based project as well, depending on your convenience. The following are the different parts in the example:
- Domain: Core of the application. Create a Cake class with its attributes, to keep it simple we will just add name here.
Java
public class Cake implements Serializable {
private static final long serialVersionUID
= 100000000L;
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this .name = name;
}
@Override
public String toString()
{
return "Cake [name=" + name + "]" ;
}
}
|
- Inbound port: Define an interface through which our core application will enable its communication. It exposes the core application to the outside world.
Java
import java.util.List;
public interface CakeService {
public void createCake(Cake cake);
public Cake getCake(String cakeName);
public List<Cake> listCake();
}
|
- Outbound port: Create one more interface to create or access the outside world i.e., Cake.
Java
import java.util.List;
public interface CakeRepository {
public void createCake(Cake cake);
public Cake getCake(String cakeName);
public List<Cake> getAllCake();
}
|
- Primary Adapters: A controller could be our primary adapter which will provide endpoints for creating and fetching the resources.
Java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping ( "/cake" )
public class CakeRestController implements CakeRestUI {
@Autowired
private CakeService cakeService;
@Override
public void createCake(Cake cake)
{
cakeService.createCake(cake);
}
@Override
public Cake getCake(String cakeName)
{
return cakeService.getCake(cakeName);
}
@Override
public List<Cake> listCake()
{
return cakeService.listCake();
}
}
|
We can create one more interface for CakeRestUI as follows:
Java
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public interface CakeRestUI {
@PostMapping
void createCake( @RequestBody Cake cake);
@GetMapping ( "/{name}" )
public Cake getCake( @PathVariable String name);
@GetMapping
public List<Cake> listCake();
}
|
- Secondary Adapters: This will be the implementation of an outbound port. Since CakeRepository is our outbound port, so let’s implement it.
Java
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Repository;
@Repository
public class CakeRepositoryImpl
implements CakeRepository {
private Map<String, Cake> cakeStore
= new HashMap<String, Cake>();
@Override
public void createCake(Cake cake)
{
cakeStore.put(cake.getName(), cake);
}
@Override
public Cake getCake(String cakeName)
{
return cakeStore.get(cakeName);
}
@Override
public List<Cake> getAllCake()
{
return cakeStore.values().stream().collect(Collectors.toList());
}
}
|
- Communication between the core to the Data Source: Finally, let’s create an implementation class that will be responsible for communication between core application to the data source using an outbound port.
Java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CakeServiceImpl
implements CakeService {
@Autowired
private CakeRepository cakeRepository;
@Override
public void createCake(Cake cake)
{
cakeRepository.createCake(cake);
}
@Override
public Cake getCake(String cakeName)
{
return cakeRepository.getCake(cakeName);
}
@Override
public List<Cake> listCake()
{
return cakeRepository.getAllCake();
}
}
|
We have finally implemented all the required methods in the given example. The following is the output on running the above code:

Now, lets create some Cake for the above example using the REST API. The following API is used to push the cakes into the repository. Since we are creating and adding the data, we use the POST request. For example:
- API: [POST]: http://localhost:8080/cake
Input Body
{
"name" : "Black Forest"
}
- API: [POST]: http://localhost:8080/cake
Input Body
{
"name" : "Red Velvet"
}
- API: [GET]: http://localhost:8080/cake
Output
[
{
"name": "Black Forest"
},
{
"name": "Red Velvet"
}
]
Advantages of the Hexagonal architecture:
- Easy to maintain: Since the core application logic(classes and objects) is isolated from the outside world and it is loosely coupled, it is easier to maintain. It is easier to add some new features in either of the layers without touching the other one.
- Easy to adapt new changes: Since all the layers are independent and if we want to add or replace a new database, we just need to replace or add the database adapters, without changing the domain logic of an application.
- Easy to test: Testing becomes easy. We can write the test cases for each layer by just mocking the ports using the mock adapters.
Similar Reads
Hexagonal Architecture - System Design
Hexagonal Architecture, also known as Ports and Adapters Architecture, is a design pattern used in system development. It focuses on making software flexible and adaptable by separating the core logic from external dependencies, like databases or user interfaces. In this approach, the core system co
15 min read
Blackboard Architecture
Blackboard architecture, also known as the blackboard system, is a problem-solving approach that utilizes a modular and decentralized framework. It effectively solves complex problems that lack a well-defined algorithm or a pre-determined architecture. Blackboard architecture is inspired by human ex
2 min read
One Dimensional Array in Java
An array is a type of Data Structure that can store collections of elements. These elements are stored in contiguous memory locations and the it provides efficient access to each element based on the index of the array element. In this article, we will learn about a one-dimensional array in Java. Wh
7 min read
Collections in Java
Any group of individual objects that are represented as a single unit is known as a Java Collection of Objects. In Java, a separate framework named the "Collection Framework" has been defined in JDK 1.2 which holds all the Java Collection Classes and Interface in it. In Java, the Collection interfac
15+ min read
Architectural Design - Software Engineering
The software needs an architectural design to represent the design of the software. IEEE defines architectural design as "the process of defining a collection of hardware and software components and their interfaces to establish the framework for the development of a computer system." The software t
4 min read
Control Abstraction in Java with Examples
Our aim is to understand and implement Control Abstraction in Java. Before jumping right into control abstraction, let us understand what is abstraction. Abstraction: To put it in simple terms, abstraction is nothing but displaying only the essential features of a system to a user without getting in
3 min read
Architecture of a System
Architecture is a critical aspect of designing a system, as it sets the foundation for how the system will function and be built. It is the process of making high-level decisions about the organization of a system, including the selection of hardware and software components, the design of interfaces
4 min read
Arrays in Java
Arrays in Java are one of the most fundamental data structures that allow us to store multiple values of the same type in a single variable. They are useful for storing and managing collections of data. Arrays in Java are objects, which makes them work differently from arrays in C/C++ in terms of me
15+ min read
Abstract Class in Java
In Java, abstract class is declared with the abstract keyword. It may have both abstract and non-abstract methods(methods with bodies). An abstract is a Java modifier applicable for classes and methods in Java but not for Variables. In this article, we will learn the use of abstract classes in Java.
10 min read
Heap implementation in Java
A heap is a binary tree-based data structure that adheres to a heap property. In a heap, every parent node has a specific relationship with its children: in a max-heap, each parent is greater than or equal to its children, while in a min-heap, each parent is less than or equal to its children. Heaps
8 min read