Hexagonal Architecture in Java

Overview

Would we rather choose to work with the code which is properly segregated into different parts (Core logic and additional services) or the messed-up code? Well, a skilled programmer will always choose the former. The workaround for this is Hexagonal architecture.

What is the Hexagonal Architecture?

Hexagonal architecture is a design pattern in Java. It represents the application as a Hexagon. At the center of the hexagon, entire core business logic is present. The hexagonal edges act as input and output elements.

The edges could be a user interface, database connection, messaging services, web services, etc.

Therefore, the core business logic of the application is segregated from outside concerns. Here, ports and adapters act as a communication medium between core logic and external interfaces.

Ports

There are two types of ports – Inbound and Outbound.

Inbound port exposes the core logic to the external services. In other words, exposes the center of the hexagon to its edges.

However, the outbound port works as a path from external services to the core logic. The core of the application uses these ports to communicate to external services like Databases, UI, etc.

In short, the inbound port and outbound port allows the inbound and outbound flow of the application respectively.

Adapters

Adapters are the services that fit into the inbound and outbound ports.

In this code example, the primary adapter is the REST service which interacts with the inbound port i.e. the service interface.

On the other hand, the secondary adapter is the implementation of an outbound port i.e. connection to the database API with the data repository.

Implementation of Hexagonal Architecture

Let’s try to understand the Hexagonal architecture principles using an example.

Consider a Book Store application with the following features:

  • Buying a book by ISBN
  • Adding a book in the store
  • Listing all the available books

Let’s see all the objects of this application categorized as Hexagonal principles:

The Core Logic

The core logic is present at the center of the hexagonal architecture. For example, we have a Book object as the core of the application:

public class Book {

    private String name;
    private String isbn;
    private String author;

    @Override
    public String toString() {
        return "Book [name=" + name + ", ISBN=" + isbn + ", author=" + author + "]";
    }

}

Inbound Port

We will have a BookService to expose the core logic of the application:

public interface BookService {

    public void addBook(Book book);

    public Book buyBook(String isbn);

    public List<Book> listBooks();

}

Outbound Port

Let’s assume, we have an external system for adding and accessing books. The core application will use an outbound port so that it can communicate to the external service:

public interface BookRepository {

    public void add(Book book);

    public Book buy(String isbn);

    public List<Book> list();

}

Primary Adapter

We will now define the REST controller to allow the user to communicate with our application:

@RestController
public class BookController {

    @Autowired
    private BookService bookService;

    @RequestMapping("/book/add")
    @PostMapping(produces = { MediaType.TEXT_PLAIN_VALUE })
    public void addBook(@RequestBody Book book) {
        bookService.addBook(book);
    }

    public Book buyBook(@PathVariable String isbn) {
        return bookService.buyBook(isbn);
    }

    public List<Book> listBooks() {
        return bookService.listBooks();
    }

}

Secondary Adapter

Now, we will implement the outbound port. In this application, BookRepositoryImplΒ is our secondary adapter:

@Repository
public class BookRepositoryImpl implements BookRepository {

    private Map<String, Book> bookMap = new HashMap<>();

    @Override
    public void add(Book book) {
        bookMap.put(book.getIsbn(), book);
    }

    @Override
    public Book buy(String isbn) {
        return bookMap.get(isbn);
    }

    @Override
    public List<Book> list() {
        return bookMap.values()
            .stream()
            .collect(Collectors.toList());
    }

}

Accessing Database API

Let’s provide an implementation for the core service BookService:

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookRepository bookRepository;

    @Override
    public void addBook(Book book) {
        bookRepository.add(book);
    }

    @Override
    public Book buyBook(String isbn) {
        return bookRepository.buy(isbn);
    }

    @Override
    public List<Book> listBooks() {
        return bookRepository.list();
    }

}

Advantages

  • The core logic is isolated from the external services as a result code is less cohesive
  • Replacement of one adapter with the other is easy without affecting the core logic
  • Following the hexagonal architecture, the application becomes very flexible to connect with other channels
  • The higher degree of maintainabilityΒ is achieved as a result of segregation of code into the inside and outside parts

Conclusion

In this article, we have explored the principles of Java Hexagonal architecture with the help of an example.

To sum up, Hexagonal architecture divides the code into the inside and outside parts which result in a lot of advantages including better flexibility and maintainability.

Newsletter Updates

Enter your name and email address below to subscribe to our newsletter

Leave a Reply