Hexagonal Architecture.

Hexagonal Architecture.

Removing technical debt step by step.

Posted by Piotr Majcher on March 29, 2019

Hexagonal architecture is nothing new. We can achieve its main principle by using basic language mechanisms and a proper package structure. On top of that, it’s relatively cheap to do that. Especially when we compare it to the cost of the potential technical debt that might appear because we failed to follow good practices when developing our IT project. Still, hexagonal architecture isn’t a very popular technique today. So, let’s start with some sample legacy code and see how we could refactor it to follow the hexagonal architecture principles.

Note: In this example, we use Java and Spring but this technique is language and framework agnostic.

From plain old sample project to Hexagonal Architecture

To make our sample project simpler, let’s assume that we’re developing an application that processes a single resource - messages. We’re exposing one method over HTTP: get all messages. We can start now with the green field project that has a well-known structure:

Hexagonal Architecture

Inside you’ll find some very simple code. Let’s start from the service, our core component:

@Service
public class MessageService {
   private final MessageRepository messageRepository;

   public MessageService(MessageRepository messageRepository) {
       this.messageRepository = messageRepository;
   }

   public List findAll() {
       return messageRepository.findAll();
   }
}

and then repository:

interface MessageRepository extends MongoRepository {
}

Dependency Inversion

What’s wrong with that? Our message service uses MessageRepository directly, so it depends on it. It breaks the last SOLID principle - Dependency Inversion. How can we get rid of this dependency? Here’s the answer: we can invert it. Let’s define the interface in a core (service) package.

public interface MessageRepository {
    List findAll();
}

We can then implement it in persistence package using files, MongoDB or any other storage. Now the persistence - the low level technical component - depends on a high level abstraction.

Driven port

What did we just do from the Hexagonal Architecture perspective? We have created a secondary (driven) port - MessageRepository - and a secondary adapter - for instance, MongoMessageRepository. Now persistence depends on domain, not the other way round. It’s worth to mention here that MessageRepository acts as Service Provider Interface (SPI).

Driver port

We already saw the secondary (driven) port and adapter. What about the primary one? The primary (driver) port serves as the definition of API domain exposed by the domain to external world. In our case, it’s the MessageService. Perhaps MessageFacade sounds better? But the name isn’t that important - rather, it’s the fact that this is the only entry point to the domain. The API is called by the primary adapter - in our sample, the MessageController. To summarize:

The primary port is API and it’s called by the primary adapter.
The secondary port is SPI and it’s implemented by the secondary adapter.

What else could we improve?

Package structure

Packages are a powerful feature of Java. The package structure should tell us something about the project. What we can say based on the package structure from picture 1 at a first glance? No more than the fact that we use layered architecture. Let’s take a look how can we improve it to follow the hexagonal architecture approach:

Hexagonal Architecture

Now the first meaningful part of the package (skipping company’s domain and project name) contains information about the main business concept - the message service. At this stage, it’s not important if we have controllers and persistency; these are technical details. Digging deeper, we see two main packages - the domain containing entire logic and adapters; the external components dependent on the domain.

Hexagonal Architecture in real project

The example showed previously is a bit verbose. If you know which component plays which role, the package structure can be simplified and still provide the separation required by hexagonal architecture.

Hexagonal Architecture

Summary

Keeping the core domain separated is crucial, especially for larger projects. If you’re working on a simple CRUD, perhaps the layered architecture is all you need. On the other hand, the goal of providing hexagonal architecture forces developers to think about the boundaries in the application. And that’s always good. Even if the application they’re working on doesn’t require sophisticated architecture, keeping these concepts in mind is always beneficial in the long-term perspective.

Sign up for our newsletter

Subscribe to stay updated with the latest e-mobility and software development releases

Contact us

We believe communication is one of the most important things in software development world. If you have any questions or want to discuss about your project leave us a message and we’ll get back to you as soon as we can!

We can help you to estimate work, as well as present our portfolio or briefly discuss idea you have.

contact@solidstudio.io

+48 795 149 398

Thanks! Sorry, something went wrong.
close
Thank you for your message!
close
Thank you for your message!
close
close
We will be in touch as quickly as we can, usually within 24 hours.