Hexagonal Architecture for absolute beginners.
Recently I was working on my new project. But this time I wanted to use something different from Controller - Service - Repository architecture because it binds application to specific tech stack or framework. So this time I decided to write it in Hexagonal Architecture. In this blog I’ll discuss this architecture and do a simple implementation in Golang.
Layered Architecture:
This architecture is also called Controller-Service-Repository architecture, and it is the most common architecture when building APIs. It has 3 major components.
Controller: Your APIs and handlers that receive, validate and deliver requests to the service layer.
Service: This layer holds all the business logic.
Repository: This layer is used to access the database and is responsible for providing data to the service layer.
This architecture focuses on code separation, and it has one-direction flow only. So it is good for small CRUD applications. Although complex applications can also be made with this architecture, there are other better ways, and hexagonal architecture is one of them.
Hexagonal Architecture
This architecture also focuses on 3 components.
Domain – This is your core application and does not have any framework code.
Ports – These are like interfaces which allow external components such as frameworks, databases, etc., to connect with your domain.
Adapters – These are the actual implementations that connect external systems to your ports.
Let’s understand this with a diagram and example.
In the middle we have Domain (core application), which is completely independent of external systems. It does not have any external system-specific code.
Our domain is guarded by ports. Think of these as a tunnel through which our application will connect to an external system.
After that we have actual external systems. These systems are using ports to communicate with the application.
With this separation our application is actually independent of external systems. Any external system can connect with the application if it satisfies the functionality defined by ports. This makes our system highly portable. We can replace any external component by using ports.
Let’s understand this by writing a simple Golang program in it.
Implementation
We have a simple folder structure with 3 folders.
Domain – Contains core application entities.
Port – Contains ports that allow external systems to connect with our core application.
Adapters – Actual external systems.
Create a new file users.go in domain folder and add following code to it.
You can see that our User struct does not have external system code. It’s just the core of our application.
Now create another file, user.go inside the ports folder with the following content.
Here you can see that we defined UserService & UserRepo. We will focus more on UserRepo because UserService will have our business logic and nothing special to Hexagonal Architecture.
So if any external database needs to interact with a user of our application, it must implement this UserRepo. Inside the adapters folder, let’s have its implementation in PostgreSQL.
I’ve created a folder postgres inside adapter and a file db.go with the following content.
This piece of code is responsible for creating a new database connection.
After this we can create another file user_postgres_repo.go in same folder which will have code to implement our UserRepo port.
You can now see that UserPostgresRepo is fulfilling our port requirement by implementing UserRepo port.
Now we can create our Service layer inside service folder.
Our service layer does not depend on a particular database instead it depends on the port, so any external database satisfying the port can be used.
Putting all things together give us this main.go file.
This was the very minimal example of Hexagonal Architecture.
But isn’t this the same as layered architecture?
It is common to think that it is the same as layered architecture because of the implementation you saw above, but the implementation we did is just a simple way to demonstrate it.
Here are the notable differences:
Direction of dependency
Layered: Domain (core app) depends on external frameworks.
Hexagonal: External frameworks depend on domain. It is the reverse of layered architecture.
Who owns the interface?
Layered: The interface is usually defined in the repository layer.
Hexagonal: The interface is defined in the domain layer. This changes everything. Domain tells the outside world what it needs, not vice versa.
Multiple adapters support
Layered: Controllers are tightly tied to HTTP. Repositories are tied to DB. You can’t easily add:
gRPC
GraphQL
Kafka, etc.
Hexagonal: Simply add multiple adapters satisfying the port and application, and it will work.
Domain purity
Layered: This service layer will contain DTOs, HTTP exceptions, database concerns, transactions, framework annotations, etc.
Hexagonal: Domain contains only pure business logic; nothing else.
Testability
Layered: You test controllers and services using framework tools (JUnit + Spring context, HTTP mocks). Even if interfaces exist, dependencies still leak.
Hexagonal: Testing is much better in this because everything is separated.
When do you need Hexagonal?
Multiple ways to enter/exit (HTTP + gRPC + Kafka)
Independent domain logic
Clean separation of concerns
Easy unit testing
Replaceable DB or queue systems
Long-term maintainability
Microservices
Conclusion
Now you have an idea of hexagonal architecture, and you can try building your next project through it. This post was just a simple introduction to hexagonal architecture. If you have suggestions and corrections for this post, please comment down. You can always subscribe for free, and I’ll reach your mailbox.
Thank you.













