A Quick and Practical Example of Hexagonal Architecture in Java
Software design patterns in action
1. Overview
Hexagonal Architecture is a software design pattern first introduced by Alistair Cockburn. It provides an opinionated way of designing a web application architecture using Java or Java-related frameworks such as Spring. In this article, we’ll look into the hexagonal architecture in Java and show its usage with the help of a practical example implemented using the Spring framework.
2. Hexagonal Architecture
The main idea behind hexagonal architecture is to have clear segregation of logic via the interface segregation principle. Moreover, the hexagonal architecture creates loosely coupled applications through the concept of ports and adapters. Here the ports refer to the interface and adapters refer to its implementation class (as shown in the figure below).

The above diagram shows various endpoints in typical hexagonal architecture. Through the application layer, the user interacts with the core application logic via ports and adapters. The application layer consists of a GUI client, Social media client, HTTP/API calls, etc. Similarly, the other half of the application shows a persistence or infrastructure layer through which the data flows out to other components. The persistence layer contains external components like databases, mailing and messaging queues, etc.
3. Example
To better understand the hexagonal architecture, Let’s consider an example of a pizza service application. In this application we will have the following features:
- List all the Pizzas available
- Insert a new Pizza into the database
- Get the Pizza by Name
4. Domain Object
The domain or entity object is the core part of the hexagonal architecture. It can have both state and behavior. This object doesn’t have any dependency on any of the application components. Any change in domain object will occur if there’s a change in the business requirement itself.
public class Pizza {
private String name;
private int price;
private String[] toppings;
// code for getters & setters
}
5. Ports
Ports in hexagonal architecture refer to the interfaces that allow inbound or outbound flow. An inbound port exposes the core application functionality to the outside world. For example, an API call to the service interface.
Let’s define a PizzaService interface that will expose its functionality to the outside components (like API calls). This is our inbound port.
public interface PizzaService {
public void createPizza(Pizza pizza);
public Pizza getPizza(String name);
public List<Pizza> laodPizza();
}
Similarly, the outbound ports are used to connect to some external repositories like databases.
Let’s define a PizzaRepository that will access the external persistent system (DB).
public interface PizzaRepo {
public void createPizza(Pizza pizza);
public Pizza getPizza(String name);
public List<Pizza> getAllPizza();
}
6. Adapters
Adapters refer to the implementation classes of their respective ports in a hexagonal architecture. They’re the outside part of the application (such as GUI, API calls, Webviews, Dao, etc.) and interact with the application via inbound and outbound ports respectively. In addition, the adapters make it simple to swap out a specific layer of the application. Depending on the required changes, we just need to add an adapter implementing an input or output port.
6.1 Primary Adapters
They’re also known as input or driving adapters as they drive the application by invoking the core part of the application using inbound ports.
Let’s define PizzaRestContoller as a REST controller as our primary adapter. It provides endpoints for creating and fetching pizzas and also implements PizzaRestUI (Webview). In addition to that, it uses PizzaService (inbound port) to invoke different methods.
@RestController
@RequestMapping(value="/pizza")
public class PizzaRestController implements PizzaRestUI {
@Autowired
private PizzaService pizzaService;
@Override
public void createPizza(@RequestBody Pizza pizza) {
pizzaService.createPizza(pizza);
}
@Override
public Pizza getPizza(@PathVariable String name) {
return pizzaService.getPizza(name);
}
@Override
public List<Pizza> listPizza() {
return pizzaService.laodPizza();
}
}
6.2 Secondary Adapters
They are known as output or driven adapters and implement an outbound port interface. These adapters provide an implementation for accessing the secondary components of an application like databases, messaging queues, etc. While the service layer implements the input port, an output port is implemented using the persistence layer.
In our case, PizzaRepoImpl is the outbound adapter that implements PizzaRepo (Outbound port).
@Repository
public class PizzaRepoImpl implements PizzaRepo {
private Map<String, Pizza> pizzaStore = new HashMap<String, Pizza>();
@Override
public void createPizza(Pizza pizza) {
pizzaStore.put(pizza.getName(), pizza);
}
@Override
public Pizza getPizza(String name) {
return pizzaStore.get(name);
}
@Override
public List<Pizza> getAllPizza() {
return pizzaStore.values().stream().collect(Collectors.toList());
}
}
Next, We can test the GET & POST endpoints using any API testing tool like Postman.
We can test the POST endpoint through http://localhost:8080/pizza-service/pizza/
{
"name" : "Margherita",
"price": "25",
"toppings" : ["tomato","onion","cucumber","jalapeno"]
}
Similarly, we can test the GET endpoint through http://localhost:8080/pizza-service/pizza/Margherita
{
"name": "Margherita",
"price": 25,
"toppings": [
"tomato",
"onion",
"cucumber",
"jalapeno"
]
}
7. Benefits
The hexagonal architecture has several advantages over traditional layered architecture such as:
- It simplifies architecture design by separating the application's internal and external components
- The core business logic is separated from any external dependencies, resulting in a high degree of decoupling
- The ports-based architecture allows our application to adapt to new channels or use new communication protocols with ease which is useful in developing domain-driven applications
8. Conclusion
In this article, we've learned about the hexagonal architecture in Java through a simple example implemented in the Spring framework. Furthermore, we've discussed some of the advantages of using hexagonal architecture over traditional layered architecture.
The code for the given example is available over on Github.