Implementation

This application manages a lyric’s storage system. It stores the related artist and a lyrics text. We can then access an endpoint which will randomly show a certain lyric and the related artist. We can also perform all other POST, PUT, DELETE and GET operations to perform CRUD(Create, Read, Update, Delete) operations via JPA(Java Persistence API) repositories. I purposely made this application simple and with the common operations. It is important to understand concepts like core, domain and infrastructure and this is also the reason why I created this application with all of these operations.

Hexagonal Architecture with REST services

Structure

In the previous points I’ve mentioned a few keywords that are important in order to set up our application. Since this is a demo application, a few considerations are important. I want the application to be simple, but also to represent what most applications do at it’s core. Most applications have a persistence framework, a business model and a presentation layer. In this example I have chosen Spring in order to use the MVC(Model View Controller) pattern. If you want to know more about the MVC [2]pattern please follow the links below in the references section. To run the application we are going to use Spring Boot. To access our database I am using JPA repositories and finally I have chosen an H2 in memory database. You will also see in that I’m using JUnit Jupiter [3], Mockito [4] and AssertJ [5]. These are off the scope of these tutorial, but if you are interested in learning more about these frameworks, then please follow the links below in the references section.

Domain

Let’s have a look at what we need as a domain. Domain is in this case is anything that we can share between the core of the application and the ports.

The first thing to do is to define how we want data to be transferred around. In our case we do this via a DTO (Data Transfer Object):

Normally you may need an exception that can be propagated throughout your architecture. This is a valid, but also very simplistic approach. Further discussion on this would require a new article and it goes off the scope of this article:

Furthermore, this is where you create your outbound port. In our case and at this point we know we want persistence, but we are not interested in how it’s implemented. This is why we only create an interface at this point.

Notice that our interface declares all necessary CRUD methods.

Core

Core works hand in hand with Domain. Both of them could be incorporated into one single module. However, this separation is very important because it makes core an implementation of only the business logic.

Core is where we find our service interface:

And its implementation:

Changes in core also represent changes in the business logic and this is why, for small applications there isn’t really a reason to further separate the port and the adapter into different modules. However, increased complexity may lead to splitting up the core into other different cores with different responsibilities. Take note that external modules should only use the interface and not the implementation of it.

JPA

Let’s create an entity. First, we should create an entity that reflects the data we want to save. In this case we need to think about the participatingArtist and the lyrics themselves. Because it’s an entity, it also needs its ID. Please note that Artist is a field that could be set into another entity. I’m not doing that in this example because it would add further complexity and another ER(entity relationship) database paradigm, which is off the scope:

Now we can implement our JPA repository implementation. It will be our outbound port. This is where our CRUD lives:

Finally, we can implement our port. This is a step between the core and the JPA repository. This is our adapter and it’s the implementation of how we want to access our JPA repository:

This completes our application implementation on the right side. Note that I’ve implemented the update operation very simplistically. If the coming DTO already has a parallel via the participatingArtist then update the lyrics. If the coming DTO already has a parallel via the lyrics then update the participatingArtist. Also notice the getLyricsById method. It will throw the domain defined LyricsNotFoundException if the lyrics with the specified ID do not exist. All mechanisms are in place to access the database. Next we are going to see the implementation of a REST service which uses the inbound port to upstream data to the application.

REST

I used the typical way to implement a rest service using the Spring MVC framework. Essentially all we need is firstly an interface to define what we need in our requests. This is, in other words, our inbound port:

And finally its implementation:

Here we have a complete typical rest service implemented where we can create lyrics, update lyrics, delete lyrics and read lyrics. We can do the latter in three different ways. We can read all of them, get one by id or just get one randomly. Application wise we have everything ready. What we still don’t have at this point is the Spring environment and the Spring Boot Launcher. Let’s have a look at this next.

Spring Boot

Our application needs a launcher to get started. This is accomplished with Spring Boot:

Then we need to configure our environment and make sure that Spring Boot is aware of H2 and the JPA environment. You do this in the application.properties file:

Luckily for us spring will look for the schema file with the name schema.sql. So let’s create our very basic schema:

spring, also looks for data.sql. So let’s put in some data:

We are now ready. Now it’s all about starting the application and making tests. All methods should be easily testable via curl or postman. As an example you can use curl to get a random lyric:

$ curl localhost:8080/lyrics/random

{“lyrics”:”Chest always so puffed guy”,”participatingArtist”:”Billie Eilish”}

Conclusion

This has been an exercise to mostly understand the hexagonal architecture principles. Using Spring is just one possibility. You can implement this architecture with other languages. You can find original examples in C#, Python, Ruby and many other languages. In the Java landscape you can do this also with any EE framework like JavaEE, JakartaEE or any other enterprise framework. The point is to always remember to isolate the inside from the outside and make sure that the communication is clear via ports and the implementation via adapters remains independent of the application core.

I have implemented this application with different modules which represented different responsibilities. However, you may also implement this in a single module. You can try that and you will see that the principles don’t really change. The only difference is that separate modules allow you to make changes independently and allows you to create different versions of your modules. In one single module you will have to make releases of everything at the same time as a follow up to code changes. However, both ways respect and follow this architecture, because in the end the point is to make interfaces and to use them in the data streaming instead of their implementations. Their implementations will be used underwater, and they are interchangeable without affecting the inside also known as application core.

I have placed all the source code of this application in GitLab.

I hope that you have enjoyed this article as much as I enjoyed writing it.

I’d love to hear your thoughts on it, so please leave your comments below.

Thanks in advance for your help and thank you for reading!

References

[1] Hexagonal Architecture by Alistair Cockburn

[2] Serving Web Content with Spring MVC

[3] JUnit Jupiter 5 User Guide

[4] Mockito