Estrutura de projetos
Esta aula aborda a estrutura idiomática de projetos em Go, explicando a organização de diretórios como cmd/ e internal/, além de boas práticas para manter o código modular e reutilizável.
Em Go, a organização do projeto segue convenções que facilitam a legibilidade, o build e o reuso. Diferente de outras linguagens, Go não impõe uma estrutura rígida, mas a comunidade adotou padrões que se provaram eficientes. Nesta aula, vamos explorar o layout idiomático, os diretórios especiais internal/ e cmd/, além de boas práticas para estruturar seus projetos.
Entender esses padrões é essencial para trabalhar em equipe, publicar pacotes e manter a sanidade mental em projetos maiores. Vamos começar com o layout geral.
Layout idiomático
O layout idiomático de um projeto Go geralmente segue a estrutura abaixo, recomendada pela comunidade e adotada em muitos projetos open source:
meu-projeto/
├── cmd/
│ └── meu-app/
│ └── main.go
├── internal/
│ └── meu-pacote/
│ └── arquivo.go
├── pkg/
│ └── publico/
│ └── util.go
├── api/
│ └── swagger.yaml
├── web/
│ └── templates/
├── configs/
│ └── config.yaml
├── scripts/
│ └── build.sh
├── test/
│ └── integration/
├── go.mod
├── go.sum
├── Makefile
└── README.mdEsse layout separa claramente as responsabilidades: cmd/ contém os pontos de entrada (main), internal/ para código privado, pkg/ para código público reutilizável, e api/, web/, configs/ para recursos auxiliares. Nem todo projeto precisa de todos esses diretórios; o importante é manter consistência.
internal/
O diretório internal/ é especial no Go: ele define um limite de visibilidade. Pacotes dentro de internal/ só podem ser importados por código que esteja na árvore pai do diretório internal. Por exemplo, se seu projeto tem a estrutura:
projeto/
├── internal/
│ └── db/
│ └── conexao.go
└── cmd/
└── server/
└── main.goO pacote projeto/internal/db pode ser importado por projeto/cmd/server/main.go, mas não por um pacote externo como github.com/outro/projeto. Isso é útil para encapsular detalhes de implementação que não devem ser expostos publicamente. Exemplo de código:
// internal/db/conexao.go
package db
import "database/sql"
var db *sql.DB
func Init(connStr string) error {
var err error
db, err = sql.Open("postgres", connStr)
return err
}
func GetDB() *sql.DB {
return db
}Assim, apenas o código dentro do mesmo módulo (ou subdiretórios) pode acessar esses pacotes, promovendo encapsulamento.
cmd/
O diretório cmd/ agrupa os pontos de entrada do seu projeto. Cada subdiretório dentro de cmd/ representa um binário diferente. Por exemplo, um projeto pode ter cmd/server/main.go e cmd/cli/main.go. Cada um terá seu próprio package main e função main(). Exemplo:
// cmd/server/main.go
package main
import (
"fmt"
"meu-projeto/internal/server"
)
func main() {
srv := server.New()
fmt.Println("Servidor iniciado na porta 8080")
srv.Run()
}Essa separação permite compilar cada binário independentemente com go build ./cmd/server ou go build ./cmd/cli. É uma prática recomendada ter um único main.go por subdiretório, e manter a lógica de negócio fora de cmd/.
Boas práticas
Aqui estão algumas boas práticas para estruturar projetos Go:
- Evite grandes pacotes: quebre em pacotes menores com responsabilidades únicas.
- Use nomes descritivos: o nome do pacote deve ser o mesmo do diretório.
- Não use
src/: Go não requer um diretóriosrc; coloque o código na raiz do módulo. - Prefira
internal/apkg/: só exponha o necessário empkg/. - Mantenha
cmd/enxuto: a função main deve apenas inicializar e chamar o código de aplicação. - Use
go.modcom o nome do módulo adequado: geralmente o repositório (ex: github.com/usuario/projeto). - Documente: coloque um README.md e, se possível, exemplos de uso.
Exemplo de um go.mod bem estruturado:
module github.com/meu-usuario/meu-projeto
go 1.21
require (
github.com/gorilla/mux v1.8.0
)Essas práticas facilitam a colaboração e a manutenção do código a longo prazo.
Referências
- Documentação oficial: Como escrever código Go
- Layout de módulos Go
- Padrão de layout de projetos Go (community)
- Effective Go
- DigitalOcean: Como estruturar um projeto Go
Exercícios
- Crie a estrutura de diretórios para um projeto Go que possui dois binários: um servidor HTTP e uma ferramenta CLI. Inclua um pacote interno para lógica de banco de dados e um pacote público para utilitários.
- Explique por que o código dentro de
internal/não pode ser importado por pacotes externos. Dê um exemplo. - No diretório
cmd/, quantos pacotesmainpodem existir e como compilar cada um? - Qual a diferença entre colocar código em
internal/e empkg/? Quando usar cada um? - Crie um
go.modpara um projeto que depende do pacotegithub.com/gorilla/muxe tem nome de módulogithub.com/meuapp/meuprojeto.
meu-projeto/
├── cmd/
│ ├── server/
│ │ └── main.go
│ └── cli/
│ └── main.go
├── internal/
│ └── db/
│ └── conexao.go
├── pkg/
│ └── util/
│ └── strings.go
├── go.mod
└── README.mdinternal/ só podem ser importados por código que esteja na árvore do módulo que contém o diretório internal. Por exemplo, se o módulo é github.com/meu/modulo, e há internal/foo, apenas pacotes dentro de github.com/meu/modulo podem importá-lo. Um pacote externo como github.com/outro/modulo receberia um erro de compilação. Isso permite encapsular implementações internas.main, um para cada subdiretório dentro de cmd/. Para compilar um específico, use go build ./cmd/server ou go build ./cmd/cli. O comando go build ./cmd/... compila todos.internal/ é para código privado do projeto, que não deve ser importado por outros módulos. pkg/ é para código público que pode ser reutilizado por outros projetos. Use internal/ para detalhes de implementação e pkg/ para bibliotecas que você quer disponibilizar.module github.com/meuapp/meuprojeto
go 1.21
require github.com/gorilla/mux v1.8.0