loading...

Hexagonal Architecture in Java

smartyansh profile image Anshul Bansal ・2 min read

1. Overview

In this quick article, we'll discuss Hexagonal Architecture in Java through a practical example.

2. What is Hexagonal Architecture?

In simple words, Hexagonal architecture separates software in to inside and outside components, instead of conceptual layers.
The component that remains inside usually consists of application and domain layers, along with the core business logic. Whereas, the component for the outside world consists of layers like UI and Database.

The connection between the inside and outside components realizes via abstractions called ports and implementations called adapters.

3. Benefits

  • Flexibility - with different adapters, the software can serve multiple channels
  • Testability - as mocking code is easy
  • Purity - as the core logic remains intact

4. Hexagonal Architecture in Java

In Java, interfaces are ports and implementation classes are adapters.

Let's create a simple Spring Boot App and try to register/view Employee details using the Hexagonal Architecture.

To start with, we'll create an entity named Employee.
Then, the EmployeeService class to keep the core business logic to persist data.

Here, the Employee and EmployeeService classes are part of the inside component. Therefore, we'll create ports to expose them.

First, let's create the EmployeeUIPort interface to communicate with the front-end:

public interface EmployeeUIPort {
    @PostMapping("register")
    public void register(@RequestBody Employee request);

    @GetMapping("view/{id}")
    public Employee view(@PathVariable Integer id);
}

Then, we'll create the EmployeeDBPort interface to communicate with the database:

public interface EmployeeDBPort {
    void register(String name);

    Employee getEmployee(Integer id);
}

Last, we need adapters to connect to the ports - EmployeeUIPort and EmployeeDBPort.

Let's create the EmployeeControllerAdapter class that implements the EmployeeUIPort interface and act as a RestController. Therefore, it'll act as an adapter to connect with the front-end:

@RestController
@RequestMapping("/employee/")
public class EmployeeControllerAdapter implements EmployeeUIPort {
    @Autowired
    private EmployeeServiceAdapter employeeService;

    @Override
    public void register(@RequestBody Employee request) {
        employeeService.register(request.getName());
    }

    @Override
    public Employee view(@PathVariable Integer id) {
        Employee employee = employeeService.view(id);
        return employee;
    }
}

Then, we'll create the EmployeeServiceAdapter (or EmployeeService for simplicity) class that implements the EmployeeDBPort interface and keep it as a Spring Service. So, it'll behave as an adapter to persist data in the database:

@Service
public class EmployeeServiceAdapter implements EmployeeDBPort {
    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    @Override
    public void register(String name) {
        Employee employee = new Employee();
        employee.setName(name);

        entityManager.persist(employee);
    }

    @Override
    public Employee getEmployee(Integer id) {
        return entityManager.find(Employee.class, id);
    }
}

That's it, we've successfully separated our App into the inside and outside components.

As a result, the business logic of the app will be exposed via ports and consumed via adapters from outside.

Similarly, we can create different ports and implement multiple adapters for any core service.

For instance, Messaging Service and Content Management Service.

5. Conclusion

In this quick article, we've explored the concept of Hexagonal architecture in Java.

We've seen the core business logic (inside component) can be exposed as ports via interfaces and consumed by different adapters (outside component) through implementation classes.

This approach helps in writing clear, readable code with less dependency on external technology. However, I'd suggest using this pattern of ports and adapters selectively, rather than going full hexagonal.

Posted on by:

smartyansh profile

Anshul Bansal

@smartyansh

Sr. Java Programmer. Technical Author. Interested in Grails & Groovy, Spring Boot, Hibernate and frontend technologies.

Discussion

pic
Editor guide
 

There seems to be a gap between EmployeeService and EmployeeDBPort. Presumably EmployeeService would invoke EmployeeDBPort to communicate with the database?

 

In this case EmployeeService is actually EmployeeServiceAdapter that communicates with the database.
I've named the EmployeeService as the EmployeeServiceAdapter, to let reader know that it act as an Adapter.
Thanks for pointing it out. I'll mention in the article.

 

Well, now this call chain looks just like a classic three-layer architecture. In particular the call from the controller to the DBPort (actually an implementation of the DBPort) feels somewhat "inside out". If you look at the stereotypical HA diagram, it gives the impression that all ports are external facing.

Perhaps a follow-up expanding on the differences between HA and the classical layered architecture?

Yes, it looks like a classic three-layer architecture. However, the pattern doesn't talk about the layers. Rather, it talks about ports and adapters.
HA expose interface to consume the inside component through a port and adapter is the middleware software component to interact with a port. Adapter can be in multiple technologies.

Thanks for suggesting a follow up on the comparison between HA and classical layered architecture.

 

I'd add an extra class so the Employee one doesn't get exposed to the outside.

 

Good point. We can do that too.