Go e Arquitetura Hexagonal: escrevendo código que não assusta mudar

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Conheça: seu código isolado do mundo externo

\n\n

Você já reescreveu a lógica de negócios só porque trocou o banco de dados de PostgreSQL para MongoDB? Ou acordou suando frio ao pensar que precisava adicionar um novo endpoint HTTP, e toda a aplicação é um gigantesco main.go de 3000 linhas?

\n\n

Se sim — bem-vindo ao clube. Se não — ou você programa em Go há pouco tempo, ou já usa a Arquitetura Hexagonal (também conhecida como Ports & Adapters).

\n\n

Neste artigo, vamos analisar como construir uma aplicação em Go que não tem medo de mudanças. Nem a troca de banco de dados, nem a migração de REST para gRPC, nem um novo framework farão você reescrever o núcleo do sistema. Apenas código. Apenas Go. Sem enrolação.

\n\n

O que é Arquitetura Hexagonal em palavras simples

\n\n

Imagine que sua aplicação é uma nave espacial. No centro está a sala de comando (lógica de negócios). Ao redor, portas de acoplamento. Através das portas, você conecta diferentes módulos: motores (banco de dados), sensores (requisições HTTP), comunicação com a Terra (filas de mensagens).

\n\n

Se você decidir trocar os motores — basta desacoplar os antigos e acoplar os novos. A sala de comando nem perceberá a substituição. Isso é a Arquitetura Hexagonal.

\n\n

Conceitos principais:

\n
    \n
  • Port (Porta) — interface que define como o mundo externo pode interagir com o núcleo. Por exemplo, UserRepository com métodos Save e FindByID.
  • \n
  • Adapter (Adaptador) — implementação concreta da porta. Uma porta pode ter vários adaptadores: PostgreSQLAdapter, InMemoryAdapter, MockAdapter para testes.
  • \n
  • Core (Núcleo) — lógica de negócios que não sabe nada sobre o mundo externo. Ela funciona apenas através das portas.
  • \n
\n\n

Essa abordagem oferece três superpoderes: testabilidade (do núcleo sem BD), flexibilidade (substituição de qualquer componente) e limites claros de responsabilidade.

\n\n

Por que Go é ideal para Hexagonal

\n\n

Go não impõe POO, mas fornece interfaces — a única coisa necessária para Ports & Adapters. As interfaces em Go são implícitas: uma struct satisfaz automaticamente a interface se implementar seus métodos. Isso significa que você pode substituir o adaptador sem alterar uma única linha no núcleo.

\n\n

Além disso, Go compila para binário, o que torna a arquitetura especialmente útil para microsserviços. E os testes e benchmarks integrados permitem verificar imediatamente o isolamento dos componentes.

\n\n

Estrutura do projeto: como organizar as pastas

\n\n

Não existe um padrão único, mas aqui está um esquema comprovado usado em projetos de produção:

\n\n
myapp/\n├── cmd/\n│   └── app/\n│       └── main.go          # Ponto de entrada, montagem de dependências\n├── internal/\n│   ├── core/\n│   │   ├── domain/          # Entidades, value objects\n│   │   ├── ports/           # Interfaces (portas)\n│   │   └── services/        # Lógica de negócios (casos de uso)\n│   └── adapters/\n│       ├── api/             # Adaptadores HTTP/gRPC\n│       ├── db/              # Implementações de repositórios\n│       └── mq/              # Filas de mensagens\n└── go.mod\n
\n\n

Importante: a pasta internal proíbe importação externa — ideal para proteger o núcleo. Os adaptadores ficam separados e podem ser facilmente substituídos.

\n\n

Escrevendo código: das interfaces à implementação

\n\n

Vamos criar um serviço simples para trabalhar com usuários. Começamos pelo núcleo.

\n\n

Passo 1. Domínio e portas

\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