Inverting Dependencies: A Step Towards Hexagonal Architecture
By: Murtuza Ranapur
Tags: Architecture, Dependency Inversion, Software Development, Programming, Startup

Inverting Dependencies: A Step Towards Hexagonal Architecture

How to take the first step towards hexagonal architecture from a traditional layered architecture

Photo by Glauco Zuccaccia on Unsplash

“You must understand that when you marry a framework to your application, you will be stuck with that framework for the rest of the life cycle of that application. For better or for worse, in sickness and in health, for richer, for poorer, forsaking all others, you will be using that framework. This is not a commitment to be entered into lightly.” ― Robert C. Martin, “Clean Architecture”

My younger self would’ve been surprised by this warning. Because the younger me considered knowing a framework equivalent to having a higher degree. Back then, I had just one goal, and that was finding a perfect framework and sticking with it as long as I was going to stay in this profession.

The idea of switching to a different framework to do the same thing was alien to me. But that attitude of mine didn’t last very long. Because very soon I realized how temporary things are in this industry — how every day we’re introduced to a new language or a new framework that could further reduce our work.

When I realized this, I’d often want to migrate my code to new shiny frameworks. But I couldn’t. Because the framework code was so deeply ingrained in the business logic that it’d take a humongous amount of effort to do so.

“The business rules should be the most independent and reusable code in the system.” ― Robert C. Martin, “Clean Architecture”

The answer to this problem was always in front of me — i.e., to keep domain logic decoupled — but an approach wasn’t. But recently, I had come across an architecture that provided an interesting way to solve this problem. It was called hexagonal architecture.

Illustration by Cth027 on Wikimedia Commons

The idea behind hexagonal architecture is to keep the domain or business logic at the center of your design. The domain shouldn’t have any outward dependencies. At its center, we have entities, and surrounding it are use cases. Everything is enclosed within the hexagon. Access to it is provided via the input/output ports only. So how do we achieve this?

Before I answer that, I’d like to talk a little bit about layered architecture and some of its shortcomings. Then I’ll show you how to transition from layered architecture to hexagonal architecture which will help us overcome those shortcomings and answer the question raised above.

Layered architecture

As you can see in the above diagram, the service layer resides over the top of the repository layer. So clearly, our domain code — which resides in the service layer — has an outwards dependency on the repository layer. As a result of this, we can’t start working on the service layer before we implement our repository layer.

Also because of this hierarchy, we usually start by modeling database schema —database-driven design — with the confidence that we’ve completely understood our domain, which, in fact, we never do unless we’ve implemented all of our business logic. And then we find ourselves spending hours working on our DB schema and ORM code again.

That’s not it. Remember the rant over mixing framework code with business logic in the first paragraph? That’s happening in the service layer because of ORM entities making their way into it. “What’s so bad about that?” you might ask. Well, those entities bare an exact resemblance to the table in our database — plus they contain metadata and mapping information that implies our domain code is fully aware of the persistence API and is coupled with it.

Now let’s see how moving to hexagonal architecture helps us get around these issues. To do that, we need our repository layer or persistence layer to depend on the service layer or domain layer. So, basically, we need to invert the dependencies.

To invert dependencies first, we need to have an entity that’s free of any ORM code. Next, our domain layer needs to expose a set of interfaces called ports, which our persistence layer needs to provide a concrete implementation.

It will be the persistence layer’s job to convert these domain entities to ORM-managed entities and to perform the required operation. With this, our domain logic is free from any persistence code. Check the below UML class diagram for more clarity.

This decoupling does come at a price, though. As you can see, now we also need to manage domain entities beside ORM entities.

But look at the bright side: There’s much to gain. For starters, our domain layer is completely decoupled. Also, you’re now free to make your domain entities much richer with helper methods and numerous constructors, which you couldn’t otherwise do with your ORM entities. This also allows us to apply domain-driven design (DDD) in its purest form.

Still, our domain layer won’t be completely free of all the framework code, as interfaces that are being used in the domain layer need to be provided concrete implementation.

For this, we still rely on our dependency-injection(DI) frameworks, which will satisfy the dependencies at the runtime. So all the layers will still have a dependency on the DI framework. But usually, DI frameworks have a very low footprint on the code, and we can further reduce those footprints by using methods such as construction injection.

That’s it! With this, we’ve taken our first step towards hexagonal architecture. There’s still a long way to go. But that’s it for now.