Estrutura de Projetos no Go: Práticas e Padrões

Estruture seus projetos Go para escalabilidade e clareza

Conteúdo da página

Structurar um projeto em Go de forma eficaz é fundamental para a manutenibilidade a longo prazo, colaboração em equipe e escalabilidade. Ao contrário de frameworks que impõem layouts de diretórios rígidos, o Go valoriza a flexibilidade — mas com essa liberdade vem a responsabilidade de escolher padrões que atendam às necessidades específicas do seu projeto.

project tree

Entendendo a filosofia do Go sobre a estrutura do projeto

A filosofia minimalista do Go se estende à organização do projeto. A linguagem não impõe uma estrutura específica, confiando aos desenvolvedores para tomarem decisões informadas. Essa abordagem levou a comunidade a desenvolver vários padrões comprovados, desde layouts simples para projetos pequenos até arquiteturas sofisticadas para sistemas empresariais.

O princípio-chave é simplicidade primeiro, complexidade quando necessário. Muitos desenvolvedores caem na armadilha de sobre-engenheirar sua estrutura inicial, criando diretórios profundamente aninhados e abstrações prematuras. Comece com o que você precisa hoje, e refatore conforme o projeto cresce.

Quando devo usar o diretório internal/ versus pkg/?

O diretório internal/ tem um propósito específico no design do Go: ele contém pacotes que não podem ser importados por projetos externos. O compilador do Go impõe essa restrição, tornando internal/ perfeito para lógica de aplicação privada, regras de negócio e utilitários que não devem ser reutilizados fora do seu projeto.

Por outro lado, o diretório pkg/ sinaliza que o código é destinado ao consumo externo. Coloque código aqui apenas se estiver construindo uma biblioteca ou componentes reutilizáveis que deseja que outros importem. Muitos projetos não precisam de pkg/ de todo — se seu código não está sendo consumido externamente, mantê-lo na raiz ou em internal/ é mais limpo. Ao construir bibliotecas reutilizáveis, considere aproveitar os generics do Go para criar componentes tipo-safe e reutilizáveis.

O Layout Padrão de um Projeto Go

O padrão mais amplamente reconhecido é o golang-standards/project-layout, embora seja importante notar que isso não é um padrão oficial. Aqui está como uma estrutura típica parece:

myproject/
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── worker/
│       └── main.go
├── internal/
│   ├── auth/
│   ├── storage/
│   └── transport/
├── pkg/
│   ├── logger/
│   └── crypto/
├── api/
│   └── openapi.yaml
├── config/
│   └── config.yaml
├── scripts/
│   └── deploy.sh
├── go.mod
├── go.sum
└── README.md

O diretório cmd/

O diretório cmd/ contém os pontos de entrada do seu aplicativo. Cada subdiretório representa um executável binário separado. Por exemplo, cmd/api/main.go constrói seu servidor de API, enquanto cmd/worker/main.go pode construir um processador de tarefas em segundo plano.

Melhor prática: Mantenha seus arquivos main.go mínimos — apenas o suficiente para conectar dependências, carregar a configuração e iniciar o aplicativo. Toda lógica substancial pertence a pacotes que main.go importa.

// cmd/api/main.go
package main

import (
    "log"
    "myproject/internal/server"
    "myproject/internal/config"
)

func main() {
    cfg, err := config.Load()
    if err != nil {
        log.Fatal(err)
    }
    
    srv := server.New(cfg)
    if err := srv.Start(); err != nil {
        log.Fatal(err)
    }
}

O diretório internal/

Este é onde sua lógica de aplicação privada vive. O compilador do Go impede que qualquer projeto externo importe pacotes dentro de internal/, tornando-o ideal para:

  • Lógica de negócio e modelos de domínio
  • Serviços de aplicação
  • APIs internas e interfaces
  • Repositórios de banco de dados (para escolher o ORM certo, veja nossa comparação de ORMs do Go para PostgreSQL)
  • Lógica de autenticação e autorização

Organize internal/ por funcionalidade ou domínio, não por camada técnica. Em vez de internal/handlers/, internal/services/, internal/repositories/, prefira internal/user/, internal/order/, internal/payment/, onde cada pacote contém seus manipuladores, serviços e repositórios.

Devo começar com uma estrutura de diretórios complexa?

Absolutamente não. Se você estiver construindo uma ferramenta pequena ou um protótipo, comece com:

myproject/
├── main.go
├── go.mod
└── go.sum

À medida que seu projeto cresce e você identifica agrupamentos lógicos, introduza diretórios. Você pode adicionar um pacote db/ quando a lógica do banco de dados se tornar significativa, ou um pacote api/ quando os manipuladores HTTP multiplicarem-se. Deixe a estrutura surgir naturalmente, em vez de impô-la de antemão.

Estruturas Planas versus Aninhadas: Encontrando o Equilíbrio

Uma das erros mais comuns na estrutura de projetos Go é o excesso de aninhamento. O Go favorece hierarquias rasas — geralmente uma ou duas camadas profundas. O aninhamento profundo aumenta a carga cognitiva e torna as importações incômodas.

Quais são os erros mais comuns?

Aninhamento excessivo de diretórios: Evite estruturas como internal/services/user/handlers/http/v1/. Isso cria complexidade desnecessária na navegação. Em vez disso, use internal/user/handler.go.

Nomes de pacotes genéricos: Nomes como utils, helpers, common ou base são cheiros de código. Eles não transmitem funcionalidade específica e frequentemente se tornam depósitos para código não relacionado. Use nomes descritivos: validator, auth, storage, cache.

Dependências circulares: Quando o pacote A importa o pacote B e B importa A, você tem uma dependência circular — um erro de compilação no Go. Isso geralmente sinaliza uma má separação de preocupações. Introduza interfaces ou extraia tipos compartilhados em um pacote separado.

Mistura de preocupações: Mantenha os manipuladores HTTP focados em preocupações HTTP, os repositórios de banco de dados em acesso a dados e a lógica de negócio em pacotes de serviço. Colocar regras de negócio em manipuladores dificulta os testes e acopla sua lógica de domínio ao HTTP.

Design Orientado a Domínio e Arquitetura Hexagonal

Para aplicações maiores, especialmente microsserviços, o Design Orientado a Domínio (DDD) com Arquitetura Hexagonal fornece uma clara separação de preocupações.

Como estruturar um microsserviço seguindo o DDD?

A Arquitetura Hexagonal organiza o código em camadas concêntricas com dependências fluindo para dentro:

internal/
├── domain/
│   └── user/
│       ├── entity.go        # Modelos de domínio e objetos de valor
│       ├── repository.go    # Interface de repositório (porta)
│       └── service.go       # Serviços de domínio
├── application/
│   └── user/
│       ├── create_user.go   # Caso de uso: criar usuário
│       ├── get_user.go      # Caso de uso: recuperar usuário
│       └── service.go       # Orquestração de serviço de aplicação
├── adapter/
│   ├── http/
│   │   └── user_handler.go  # Adaptador HTTP (extremidades REST)
│   ├── postgres/
│   │   └── user_repo.go     # Adaptador de banco de dados (implementa porta de repositório)
│   └── redis/
│       └── cache.go         # Adaptador de cache
└── api/
    └── http/
        └── router.go        # Configuração de rota e middleware

Camada de domínio (domain/): Lógica de negócio central, entidades, objetos de valor e interfaces de serviço de domínio. Esta camada não tem dependências em sistemas externos — nenhuma importação HTTP, nenhuma importação de banco de dados. Ela define interfaces de repositório (portas) que adaptadores implementam.

Camada de aplicação (application/): Casos de uso que orquestram objetos de domínio. Cada caso de uso (ex: “criar usuário”, “processar pagamento”) é um arquivo ou pacote separado. Esta camada coordena objetos de domínio, mas não contém regras de negócio por si só.

Camada de adaptador (adapter/): Implementa interfaces definidas por camadas internas. Manipuladores HTTP convertem solicitações em objetos de domínio, repositórios de banco de dados implementam persistência, filas de mensagens lidam com comunicação assíncrona. Esta camada contém todo o código específico de framework e infraestrutura.

Camada de API (api/): Rotas, middleware, DTOs (Objetos de Transferência de Dados), versionamento de API e especificações OpenAPI.

Essa estrutura garante que sua lógica de negócio central permaneça testável e independente de frameworks, bancos de dados ou serviços externos. Você pode trocar PostgreSQL por MongoDB ou REST por gRPC sem tocar no código do domínio.

Padrões Práticos para Tipos de Projetos Diferentes

Ferramenta CLI Pequena

Para aplicações de linha de comando, você desejará uma estrutura que suporte múltiplos comandos e subcomandos. Considere usar frameworks como Cobra para estrutura de comandos e Viper para gerenciamento de configuração. Nosso guia sobre criar aplicações CLI em Go com Cobra & Viper aborda esse padrão em detalhes.

mytool/
├── main.go
├── command/
│   ├── root.go
│   └── version.go
├── go.mod
└── README.md

Serviço de API REST

Ao construir APIs REST em Go, essa estrutura separa preocupações de forma clara: manipuladores lidam com preocupações HTTP, serviços contêm lógica de negócio e repositórios gerenciam acesso a dados. Para um guia abrangente que abrange abordagens com a biblioteca padrão, frameworks, autenticação, padrões de teste e melhores práticas para serviços de backend escaláveis, veja nosso guia completo para construir APIs REST em Go.

myapi/
├── cmd/
│   └── api/
│       └── main.go
├── internal/
│   ├── config/
│   ├── middleware/
│   ├── user/
│   │   ├── handler.go
│   │   ├── service.go
│   │   └── repository.go
│   └── product/
│       ├── handler.go
│       ├── service.go
│       └── repository.go
├── pkg/
│   └── httputil/
├── go.mod
└── README.md

Monorepo com Múltiplos Serviços

myproject/
├── cmd/
│   ├── api/
│   ├── worker/
│   └── scheduler/
├── internal/
│   ├── shared/        # Pacotes internos compartilhados
│   ├── api/          # Código específico para API
│   ├── worker/       # Código específico para worker
│   └── scheduler/    # Código específico para scheduler
├── pkg/              # Bibliotecas compartilhadas
├── go.work           # Arquivo de workspace do Go
└── README.md

Testes e Documentação

Coloque os arquivos de teste ao lado do código que testam usando o sufixo _test.go:

internal/
└── user/
    ├── service.go
    ├── service_test.go
    ├── repository.go
    └── repository_test.go

Essa convenção mantém os testes próximos à implementação, tornando-os fáceis de encontrar e manter. Para testes de integração que envolvem múltiplos pacotes, crie um diretório separado test/ na raiz do projeto. Para orientação abrangente sobre como escrever testes unitários eficazes, incluindo testes orientados por tabela, mocks, análise de cobertura e melhores práticas, veja nosso guia sobre estrutura e melhores práticas de testes unitários em Go.

A documentação pertence a:

  • README.md: Visão geral do projeto, instruções de instalação, uso básico
  • docs/: Documentação detalhada, decisões de arquitetura, referências de API
  • api/: Especificações OpenAPI/Swagger, definições protobuf

Para APIs REST, gerar e servir documentação OpenAPI com Swagger é essencial para descoberta de API e experiência do desenvolvedor. Nosso guia sobre adicionar Swagger à sua API Go aborda a integração com frameworks populares e melhores práticas.

Gerenciando Dependências com Go Modules

Todo projeto Go deve usar Go Modules para gerenciamento de dependências. Para uma referência abrangente sobre comandos Go e gerenciamento de módulos, consulte nosso Go Cheatsheet. Inicialize com:

go mod init github.com/yourusername/myproject

Isso cria go.mod (dependências e versões) e go.sum (checksums para verificação). Mantenha esses arquivos em controle de versão para builds reprodutíveis.

Atualize dependências regularmente:

go get -u ./...          # Atualizar todas as dependências
go mod tidy              # Remover dependências não utilizadas
go mod verify            # Verificar checksums

Principais Lições Aprendidas

  1. Comece simples, evolua naturalmente: Não sobre-engenheire sua estrutura inicial. Adicione diretórios e pacotes quando a complexidade exigir.

  2. Prefira hierarquias planas: Limite o aninhamento a uma ou duas camadas. A estrutura plana de pacotes do Go melhora a legibilidade.

  3. Use nomes descritivos de pacotes: Evite nomes genéricos como utils. Nomeie pacotes com base no que eles fazem: auth, storage, validator.

  4. Separe preocupações claramente: Mantenha manipuladores focados em HTTP, repositórios em acesso a dados e lógica de negócio em pacotes de serviço.

  5. Use internal/ para privacidade: Use-o para código que não deve ser importado externamente. A maioria do código de aplicação pertence aqui.

  6. Aplique padrões de arquitetura quando necessário: Para sistemas complexos, a Arquitetura Hexagonal e o DDD fornecem limites claros e testabilidade.

  7. Deixe o Go guiá-lo: Siga os idiomas do Go em vez de importar padrões de outras linguagens. O Go tem sua própria filosofia sobre simplicidade e organização.

Outigos Artigos Relacionados