Why you shouldn’t use hexagonal architecture with Go

Nor Clean Code

Robertoplazaromero
4 min readJan 9, 2022

The first steps to maintainability

If you are looking up how to structure your Go project you’re about to enter the biggest clusterfuck the language has for you. I’m here to offer you a point of view of someone who is still looking for and answer and can’t manage to find a suitable one.

One of the first talks about this topic that I ever heard was the one given by Kat Zien at GopherCon 2018 and… It’s awesome! This talk goes straight to the point, addresses and discusses the pros and cons of a lot of common ways to structure an app and gives a few examples, that’s everything a this kind of talk should do. The final conclusion is “just do what you need”, but the structure presented as “more mature” or “more convenient” was Hex-Arch with DDD Actors.

When does it go wrong? The moment you realize Go is not an Object Oriented language. First of all, yes, Go supports Object Oriented patterns and Object Oriented design, but when the language calls them structs instead of objects is, perhaps, trying to tell you something.

The Clean Code and Clean Architecture legacy

Clean Code is an absolute must read, it will change the way you program and the way you think about programming (and coding) on a positive way. The problem is that both, Clean Code and Clean Architecture are not as generic as they are meant to be, and for a good reason, despite being marketed as a universal language, both books are mainly focused on high level object oriented languages, specifically Java and C#.

A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result — Java programs are written in Java, not Go.

This Quote belongs to the official documentation of Go, section ‘Understanding Go’, post name ‘Effective Go’. The creators and designers of Go want the language to be its own framework. This idea of letting the structure of a project speak about it collides with the idea of having generic, universal structures of Clean and Hex Arch. Except it doesn’t.

The dichotomy, Generic vs Language Specific

This generic software engineering practices are good for some part, but it soon comes apparent that they handicap the abstraction capabilities of some languages. Generic code should be the set of the minimal common abstraction mechanisms if you want it to be… generic, therefore the name.

The nuances and interpretations of this form are lost along the way and only one question remains. Why have then more than one language? Simple, because all I have said is a fallacy, and Clean Architecture should not be an actual directory structure but a representation of the ‘things’ that interact with your system.

In the same way that naming service a class that calls a database doesn’t make this class a service, giving a language-friendly name to a interface that carries on business logic does not make it not a service. Mastering this is incredibly difficult, but yields the best of both worlds.

How do I structure my project then?

The answer is in the book Get Your Hands Dirty on Clean Architecture: A Hands-on Guide to Creating Clean Web Applications with Code Examples in Java. Hexagonal architecture is just a framework to help you modeling problems, but is not necessarily the way those problems should be solved.

In the examples given in the book, one of the Clean Architectures proposal splits the code into input models and output models. It may not be enough, but follow that idea. Hexagonal architecture tells us if an agent is a service, a repo, a value object… but does not speak about the way to use those agents, therefore there’s no need to use Hex Arch notation to use Hex Arch.

For example, you need to isolate the business logic from the persistence logic in your system, is not a matter of opinion. Instead of calling this units of system logic ‘services’ as in Hex Arch you should give them names related to the use case. Take for example a user service in Hex Arch, the equivalent in Go could be a set of interfaces UserAdder, UserFetcher, UserUpdater and UserDeleter if you want to have a more Object Oriented Designed you could compose them into an interface UserManager.

In the same way, instead of having a UserRepository that stores every persistence operation, declaring an interface for each one of those operations (and then conglomerate them if necessary) is, at least to me, much more desirable than to pack every operation of a model within the same struct.

There’s the catch, You should use Hexagonal Architecture but You should not use Hexagonal Architecture Explicitly. The entities and actors of Hex Arch will be there even if you don’t name them, you must know they are there, but don’t forget you are writing Go and not UML++ and therefore you should use the language to its full potential.

--

--