Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Hexagonal Architecture for Java

DZone 's Guide to

Hexagonal Architecture for Java

Learn more about how you can isolate your core logic from outside elements.

· Java Zone ·
Free Resource

Hexagonal architecture is a style that talks about layering your objects in a way that isolates your core logic from outside elements. The core logic is the piece specific to your business, and outside elements are like integration points, e.g. DBs, external APIs, UIs, and others. It divides software into the inside and outside parts. Inside parts contain Core Business logic and the Domain layer (explained in LayeredArchitecture). The outside part consists of UI, database, messaging, and other stuff. Inside and Outside parts both communicate with each other via ports and adapters.

Benefits

  • Software developed using this architecture is independent of channels and can support multiple channels
  • Easy to swap out the inbound and outbound integration points
  • Testing the software becomes easy because we can mock integration points easily

Implementation in Java

As explained above, the hexagonal architecture is more around ports and adapters. In Java, interfaces implement the ports and the implementation class works as the adapters. So, we will take a look at a simple example using the Spring Boot application and see how this style can be applied to this app.

In this application, we have the functionality to create/view Employee Details. The Core Business logic is in the EmployeeService and the domain is an EmployeeSo, these will be considered as inside parts.

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepositoryPort employeeRepository;

    public void create(String name, String role, long salary){
        employeeRepository.create(name, role, salary);
    }

    public Employee view(Integer userId){
        return employeeRepository.getEmployee(userId);
    }
}
@Entity
@Table(name = "employee")
public class Employee{
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Integer id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "role", nullable = false)
    private String role;

    @Column(name = "salary", nullable = false)
    private long salary;
    // Setter and Getter methods
}


So now, this application can expose this functionality via REST or Messaging. Hence, we have created the EmployeeControllerAdapter to expose REST endpoints, which implements the EmployeeUIPort.

@RestController
@RequestMapping("/employees/")
public class EmployeeControllerAdapter implements EmployeeUIPort{

    @Autowired
    private EmployeeService employeeService;

   @Override
    public void create(@RequestBody Employee request) {
        employeeService.create(request.getName(), request.getRole(), request.getSalary());
    }

    @Override
    public Employee view(@PathVariable Integer id) {
        Employee employee = employeeService.view(id);
        return employee;
    }
}
 public interface EmployeeUIPort {
 @PostMapping("create")
    public void create(@RequestBody Employee request);

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


As part of the business logic, EmployeeService also needs to call the DB, which is, again, an integration point (outside part), so we have created the EmployeeRepositoryPort, andEmployeeServiceAdapterimplements this port.

@Service
public class EmployeeServiceAdapter implements EmployeeRepositoryPort {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    @Override
    public void create(String name, String role, long salary) {
        Employee employee = new Employee();
        employee.setName(name);
        employee.setRole(role);
        employee.setSalary(salary);
        entityManager.persist(employee);
    }

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

    void create(String name, String role, long salary);

    Employee getEmployee(Integer userId);
}


So, we see how EmployeeService has used theEmployeeUIPortport to expose its service and  EmployeeRepositoryPort to interact with the DB. Also,  EmployeeControllerAdapter and EmployeeServiceAdapter help to integrate with REST APIs and DB.

Summary

To summarize, the hexagonal architecture is an approach used to divide the application into inside and outside parts. They are connected through ports (exposed by the inside) and adapters (implemented by the outside). So, by applying this approach, the core use case code remains intact and can serve to multiple channels, supporting different protocols. It also helps to make the application tested easily. However, I would suggest not to implement this architecture fully for the whole application but use interfaces and adapters selectively.

As always, the code of all examples above can be found over on GitHub.

Topics:
hexagonal architecture ,java ,core business logic ,ports ,adapters ,database ,spring boot

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}