A Quick and Practical Example of Hexagonal Architecture in Java

hexagonal architecture in java

1. Introduction

Hexagonal architecture is a software architecture which breaks down functionality in different components. The components or adapters communicate with the application core via the ports. The goal is to create loosely coupled components. As a result, components are easily replaced, deployed, and tested.

This architecture is an alternative to the layered architecture.

2. Design

3. Implementation

In this paragraph, we will talk about the implementation of the hexagonal architecture in java diagram. The implementation uses spring boot and Java 14. The different components are organized in packages with the HexagonalApp being the only one to have the main method.

It is a simple implementation of the architecture, modeling a user and the book list he/she reads.

The project architecture-hexagonal has the following structure:

3.1. HexagonalApp (core)

Firstly, for our hexagonal architecture in java implementaion let’s start off with the HexagonalApp class containing the main method. This is the entry point of the application. It may be found under the app package.

@SpringBootApplication
public class HexagonalApp {
    public static void main(String[] args) {
        SpringApplication.run(HexagonalApp.class, args);
    }
} 

In addition to being the entry point of the whole project, HexagonalApp holds the business logic of the application. HexService is an interface that serves as the port to connect the Rest adapter to the HexagonalApp. This port offers services to clients. In our example, the clients are a Rest component and a web client. Notably, the service provides the list of users and the list of books as shown in the code below.

public interface HexService {
    List getUsers();
    List getBooks();
} 

Its implementation is a class implementing the interface HexService. Similarly, this class is represented as a port in our architecture.

public class HexServiceImpl implements HexService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private BookRepository bookRepository;

    @Override
    public List getUsers() {
        return userRepository.findAll();
    }

    @Override
    public List getBooks() {
        return bookRepository.findAll();
    }
}

As can be seen in the code above, HexServiceImpl fetches the users and books from a spring data repository located in the Domain adapter. Identically, we can understand the role of NotifierService interface. It connects the Notifier to the core application. Its implementation may be found on the notifier component.

public interface NotifierService {
    void emailSend();
    void smsSend();
}

3.2. Rest

Rest adapter is the Respresentational State Transfer component providing web services over HTTP. All things considered, it is the client side of the architecture. It consists of a Spring Web controller using HexService as shown in the code below.

@RestController
public class HexController {

    @Autowired
    HexService hexService;

    @GetMapping
    public List getBooksForUser(){
        return hexService.getUsers();
    }
}

3.3. Domain

Generally speaking, in most cases, we need to insert and retrieve data from a database. With this in mind, we need the domain component serving as a connector to the database. Of course, it needs to communicate with the core. This happens through HexServiceImp. Below are the entities modeling a Book and a User.

@Entity
public class Book {
    @Id
    private Long id;
    private String title;
    public Book(String title) {
        this.title = title;
    }
    public Book() {
    }
    public String description() {
        return "Title: " + title;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
}
@Entity
public class User {
    @Id
    private Long id;
    private String firstName;
    private String lastName;

    @OneToMany(mappedBy = "user")
    private List books;
    public User() {
    }
    public User(Long id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Equally important are the repositories which do the actual work of fetching the data.

@Repository
public interface BookRepository extends JpaRepository<Book, String> {
    List findAll();
}

@Repository
public interface UserRepository extends JpaRepository<User, String> {
    List findAll();
}

3.4. Notifier

Especially important is to send notifications to the end users we need the Notifier implementing the NotifierService inteface.

public class NotifierServiceImpl implements NotifierService {
    @Override
    public void emailSend() {
        System.out.println("Send email here");
    }

    @Override
    public void smsSend() {
        System.out.println("Send SMS here");
    }
}

4. Conclusion

This was a quick and practical example of Hexagonal Architecture in Java. It is important to realize that this is just a practical example. Therefore, you may change it depending on the requirements. The code of the whole project may be found on github.

If you want to read more useful articles related to software development and more check the articles page here.

Leave a Comment