Go and Hexagonal Architecture: Writing Code That Isn't Scary to Change

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Meet: Your Code in Isolation from the Outside World

\n\n

Have you ever rewritten business logic just because you switched your database from PostgreSQL to MongoDB? Or woken up in a cold sweat at the thought of adding a new HTTP endpoint, only to find the entire application is one giant main.go file with 3000 lines?

\n\n

If so — welcome to the club. If not — you either haven't been writing Go for long, or you're already using Hexagonal Architecture (also known as Ports & Adapters).

\n\n

In this article, we'll break down how to build a Go application that isn't afraid of change. Neither switching databases, nor migrating from REST to gRPC, nor adopting a new framework will force you to rewrite the system's core. Just code. Just Go. No fluff.

\n\n

What is Hexagonal Architecture in Simple Terms

\n\n

Imagine your application is a spaceship. At the center is the control room (business logic). Around it are docking ports. Through these ports, you connect different modules: engines (database), sensors (HTTP requests), communication with Earth (message queues).

\n\n

If you decide to replace the engines — you simply undock the old ones and dock new ones. The control room won't even notice the swap. That's Hexagonal Architecture.

\n\n

Key concepts:

\n
    \n
  • Port — an interface that defines how the outside world can interact with the core. For example, UserRepository with Save and FindByID methods.
  • \n
  • Adapter — a concrete implementation of a port. One port can have many adapters: PostgreSQLAdapter, InMemoryAdapter, MockAdapter for testing.
  • \n
  • Core — the business logic, which knows nothing about the outside world. It only works through ports.
  • \n
\n\n

This approach provides three superpowers: testability (of the core without a database), flexibility (replacing any component), and clear boundaries of responsibility.

\n\n

Why Go is Perfect for Hexagonal Architecture

\n\n

Go doesn't enforce OOP, but it provides interfaces — the only thing you need for Ports & Adapters. Interfaces in Go are implicit: a struct automatically satisfies an interface if it implements its methods. This means you can swap an adapter without changing a single line in the core.

\n\n

Plus, Go compiles to a binary, making this architecture especially useful for microservices. And the built-in testing and benchmarking allow you to immediately verify component isolation.

\n\n

Project Structure: How to Organize Folders

\n\n

There is no single standard, but here is a proven scheme used in production projects:

\n\n
myapp/\n├── cmd/\n│   └── app/\n│       └── main.go          # Entry point, dependency wiring\n├── internal/\n│   ├── core/\n│   │   ├── domain/          # Entities, value objects\n│   │   ├── ports/           # Interfaces (ports)\n│   │   └── services/        # Business logic (use cases)\n│   └── adapters/\n│       ├── api/             # HTTP/gRPC adapters\n│       ├── db/              # Repository implementations\n│       └── mq/              # Message queues\n└── go.mod\n
\n\n

Important: the internal folder prevents imports from outside — perfect for protecting the core. Adapters live separately and can be easily replaced.

\n\n

Writing Code: From Interfaces to Implementation

\n\n

Let's create a simple service for working with users. We'll start with the core.

\n\n

Step 1. Domain and Ports

\n\n
// internal/core/domain/user.go\npackage domain\n\nimport "time"\n\ntype User struct {\n    ID        string\n    Email     string\n    Name      string\n    CreatedAt time.Time\n}\n\n// internal/core/ports/repositories.go\npackage ports\n\nimport "myapp/internal/core/domain"\n\ntype UserRepository interface {\n    Save(user domain.User) error\n    FindByID(id string) (domain.User, error)\n    FindByEmail(email string) (domain.User, error)\                

Blogs

Book Recommendations