Hexagonal Architecture in Java
Tags: java, kotlin, springboot, microservices
Published: 2021-03-12 00:29:10

---
title: Hexagonal Architecture in Java
published: true
date: 2020-05-23 03:00:06 UTC
tags: Java,Kotlin,Springboot,Microservices
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fx85x2ud6fkozqhkwv7k.jpg
canonical_url: https://theanirban.dev/hexagonal-architecture-in-java/
---
## 1. Overview

In this tutorial, we'll take a look into the **hexagonal architecture in Java**. To illustrate this further, we'll create a Spring Boot application.

## 2. Hexagonal Architecture

The hexagonal architecture describes a pattern for designing software applications around the domain logic. **The hexagon describes the core of the application consisting of the domain object and the use cases of the application**. **The edges of the hexagon provide the inbound and outbound ports to the outside parts of the hexagon** such as web interface, databases, etc.

**So, in this kind of software architecture, all the dependencies between the components point towards the domain object**. **Therefore, the communication between the core application and the outside part is only possible using ports and adapters**. In the following sections, we can have a deep dive into the different layers of hexagonal architecture.

## 3. Domain Object

**The domain object is the core part of the application**. It can have both state and behaviour. However, it doesn’t have any outward dependency. So any change in the other layers has no impact on the domain object.

**The domain object changes only if there is a change in the business requirement**. Hence, this is an example of the Single Responsibility Principle among the SOLID principles of software design.

First, let’s create a domain object **_Product_** that forms the core of the application. It contains product-related information and business validations:

```
public class Product {

    private Integer productId;
    private String type;
    private String description;

    public Product() {
        super();
    }

    public Product(Integer productId, String type, String description) {
        this.productId = productId;
        this.type = type;
        this.description = description;
    }

    //getters
}

```

## 4. Ports

The ports are interfaces that allow inbound and outbound flow. Therefore, the core part of the application communicates with the outside part using the dedicated ports.

### 4.1. Inbound Port

**The inbound port exposes the core application to the outside**. It is an interface that can be called by the outside components. **These outside components calling an inbound port are called primary or input adapters**.

Let's define a **_ProductService_** interface which is the inbound port:

```
public interface ProductService {

    List<Product> getProducts();

    Product getProductById(Integer productId);

    Product addProduct(Product product);

    Product removeProduct(Integer productId);
}

```

### 4.2. Outbound Port

**The outbound port allows outside functionality to the core application**. It is an interface that enables the use case of the core application to communicate with the outside such as database access. **Hence, the outbound port is implemented by the outside components which are called secondary or output adapters**.

Let's define a **_ProductRepository_** interface which is an outbound port:

```
public interface ProductRepository {

    List<Product> getProducts();

    Product getProductById(Integer productId);

    Product addProduct(Product product);

    Product removeProduct(Integer productId);
}

```

## 5. Adapters

The adapters are the outside part of the hexagonal architecture. So, they interact with the core application only by using the inbound and outbound ports.

### 5.1. Primary Adapters

**The Primary adapters are also known as input or driving adapters. Therefore, they drive the application by invoking the corresponding use case of the core application using the inbound ports**. For example, primary adapters are REST APIs or web interfaces.

Let's define a **_ProductController_** class as our primary adapter. In particular, it's a REST controller that provides endpoints for creating and accessing products. Subsequently, it uses the inbound port service to interact with the core application:

```
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {

    private ProductService productService;

    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping
    public ResponseEntity<List<Product>> getProducts() {
        return new ResponseEntity<List<Product>>(productService.getProducts(), HttpStatus.OK);
    }

    @GetMapping("/{productId}")
    public ResponseEntity<Product> getProductById(@PathVariable Integer productId) {
        return new ResponseEntity<Product>(productService.getProductById(productId), HttpStatus.OK);
    }

    @PostMapping
    public ResponseEntity<Product> addProduct(@RequestBody Product product) {
        return new ResponseEntity<Product>(productService.addProduct(product), HttpStatus.OK);
    }

    @DeleteMapping("/{productId}")
    public ResponseEntity<Product> removeProduct(@PathVariable Integer productId) {
        return new ResponseEntity<Product>(productService.removeProduct(productId), HttpStatus.OK);
    }
}

```

### 5.2. Secondary Adapters

**The Secondary adapters are also known as output or driven adapters. These are implementations of the outbound port interface**. The use case of the core application invokes the secondary adapters using the output port. For instance, secondary adapters are connections to the database and external API calls.

Let's define a **_ProductRepositoryImplementation_** class as our secondary adapter. In particular, this class implements the outbound port interface **_ProductRepository_** and allows the core application to access the database:

```
@Repository
public class ProductRepositoryImplementation implements ProductRepository {

    private static final Map<Integer, Product> productMap = new HashMap<Integer, Product>(0);

    @Override
    public List<Product> getProducts() {
        return new ArrayList<Product>(productMap.values());
    }

    @Override
    public Product getProductById(Integer productId) {
        return productMap.get(productId);
    }

    @Override
    public Product addProduct(Product product) {
        productMap.put(product.getProductId(), product);
        return product;
    }

    @Override
    public Product removeProduct(Integer productId) {
        if(productMap.get(productId)!= null){
            Product product = productMap.get(productId);
            productMap.remove(productId);
            return product;
        } else
            return null;

    }
}

```

**The adapters provide flexibility to the application without influencing the core application logic**. If the application can be used by a new client in addition to the existing one, we can add the new client to the inbound port.

In addition, if the application requires a different database, we can add a new secondary adapter implementing the same outbound port.

## 6. Use cases of the core application

**The use cases of the core application are the inside part of the hexagonal architecture**. They are specific use case implementations of the inbound port. **Hence, it contains all the use case-specific business rule validations and logic**. The use case has no outside dependency similar to the domain objects.

Let's define a **_ProductServiceImplementation_** class that provides the specific use case implementation:

```
@Service
public class ProductServiceImplementation implements ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Override
    public List<Product> getProducts() {
        return productRepository.getProducts();
    }

    @Override
    public Product getProductById(Integer productId) {
        return productRepository.getProductById(productId);
    }

    @Override
    public Product addProduct(Product product) {
        return productRepository.addProduct(product);
    }

    @Override
    public Product removeProduct(Integer productId) {
        return productRepository.removeProduct(productId);
    }
}

```

## 7. Conclusion

The hexagonal architecture offers several benefits compared to a layered architecture:

- It **simplifies the architecture design** by separating the inside and outside parts of the application
- The core business logic is isolated from any external dependencies which help to **achieve a high degree of decoupling**
- The **ports allow flexibility in connecting to new adapters** in the form of new web clients or databases

The hexagonal architecture might be an overhead for designing simple CRUD applications. However, this architecture is **useful when we are designing a domain-driven application**.

The code for these examples is available over on [Github](https://github.com/anirban99/hexagonal-architecture).

_Originally published on [Anirban's Tech Blog](https://theanirban.dev)_.