Seguir esse layout acelera a integração e mantém os limites dos módulos claros.
Coloque um pacote main por comando em cmd/<nome>/.
Mantenha os detalhes de implementação em internal/ para que outros módulos não possam importá-los.
Bibliotecas destinadas à reutilização externa vivem em pacotes de nível superior com nomes claros ou em pkg/ quando você deseja uma zona pública óbvia.
Um módulo pode hospedar múltiplos comandos e pacotes compartilhados; divida módulos apenas quando as linhas de release divergirem.
Cartão de receita de referência rápida - pronto para copiar e colar.
example.com/shop/
go.mod
cmd/
shopd/main.go
migrate/main.go
internal/
api/
store/
pkg/client/ # SDK estável opcional
// cmd/shopd/main.go
package main
import " example.com/shop/internal/api "
func main () {
api. ListenAndServe ()
}
Quando usar isso:
Iniciando um novo serviço HTTP ou worker com mais de um binário.
Publicando uma biblioteca consumida por outras empresas ou repositórios.
Refatorando um repositório plano onde main e SQL se misturavam em um único pacote.
example.com/notify/
go.mod
README.md
cmd/
notifyd/main.go
internal/
config/config.go
server/http.go
queue/worker.go
pkg/client/client.go
// internal/config/config.go
package config
import " os "
func Port () string {
if p := os. Getenv ( "PORT" ); p != "" {
return p
}
return "8080"
}
// internal/server/http.go
package server
import (
" net/http "
" example.com/notify/internal/config "
)
func ListenAndServe () error {
return http. ListenAndServe ( ":" + config. Port (), http. HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w. Write ([] byte ( "ok" ))
}))
}
// pkg/client/client.go - SDK fino para outros serviços
package client
import " net/http "
type Client struct { Base string }
func ( c Client ) Ping () ( * http . Response , error ) {
return http. Get (c.Base + "/healthz" )
}
// cmd/notifyd/main.go
package main
import (
" log "
" example.com/notify/internal/server "
)
func main () {
if err := server. ListenAndServe (); err != nil {
log. Fatal (err)
}
}
O que isso demonstra:
cmd/notifyd é o único ponto de entrada executável; a lógica permanece testável em internal/server.
pkg/client é opcional, mas sinaliza uma API suportada para outros módulos.
Configuração e conexão HTTP são detalhes de implementação privados.
cmd/ - Cada subdiretório é package main construindo um binário.
Os nomes correspondem ao artefato (shopd, migrate, ctl).
CI mapeia go build -o bin/shopd ./cmd/shopd.
internal/ - O compilador bloqueia importações de fora da subárvore do módulo pai.
Use para camadas de armazenamento, adaptadores e qualquer coisa não coberta por promessas semver.
pkg/ - Convenção de repositórios da era Kubernetes; não é imposta.
Importadores externos podem depender dele, então trate-o como qualquer pacote público com disciplina de compatibilidade.
Pacotes de domínio de nível superior - Muitos módulos usam example.com/widget/widget ou example.com/widget com pacotes na raiz em vez de pkg/.
Escolha uma abordagem e documente-a no README.
Layout Ideal para Cuidado Comando único + internal Microserviço pequeno Pacotes "god" em crescimento cmd/* + internal/* Múltiplos binários Flags/configuração duplicadas SDK cliente pkg/ Bibliotecas de plataforma Mudanças de quebra acidentais Monorepo multi-módulo Releases independentes Sobrecarga de go.work ou replace
# Compila todos os comandos
go build -o bin/ ./cmd/...
# Testa pacotes internos sem exportá-los
go test ./internal/...
// Evite lógica de negócios no main - mantém os testes rápidos
func main () {
if err := run (); err != nil {
log. Fatal (err)
}
}
Tratar pkg/ como magicamente estável - É apenas uma convenção; semver e documentação tornam a estabilidade real.
Tudo em internal/ - Torna os testes de integração em outros repositórios impossíveis; exporte superfícies de cliente mínimas.
Múltiplos mains em um diretório - Inválido; divida binários sob cmd/.
Repositórios planos que crescem para sempre - Sem internal/, helpers privados vazam para caminhos de importação públicos.
Copiar o layout do Kubernetes cegamente - Seu serviço pode precisar de um binário, não de operadores e CRDs.
Módulos separados por serviço - Isolamento forte em grandes organizações.
Pacotes de nível superior orientados a domínio (billing/, shipping/) sem pkg/ - Claro para bibliotecas de médio porte.
Templates (kubebuilder, cobra) - Geram layout cmd/ para domínios de problema específicos.
O Layout Padrão de Projetos Go é oficial?
Não - é uma orientação da comunidade.
O blog Go documenta módulos e pacotes; o layout é uma escolha da equipe dentro das regras do módulo.
Bibliotecas devem usar pkg/?
Não - muitos módulos exportam da raiz do módulo ou de pastas nomeadas.
Use pkg/ quando quiser uma zona de API externa óbvia.
Quantos comandos pertencem a um módulo?
Tantos quanto compartilham código e cadência de release.
Divida módulos quando binários são lançados independentemente com versões diferentes.
Onde vão os testes de integração?
testdata/, internal/..._test.go, ou diretórios test/ de nível superior.
Mantenha _test.go ao lado do código para testes unitários; use tags de build para arquivos apenas de integração.
Pacotes internos podem importar pkg/?
Sim - o fluxo de dependência é para dentro: cmd -> internal -> (opcional) pacotes públicos compartilhados.
Evite que pkg importe internal (inverte o modelo).
Onde as migrações devem ficar?
cmd/migrate, internal/migrate, ou arquivos SQL em db/migrations/.
Escolha uma ferramenta (golang-migrate, goose) e documente-a.
Configurações devem ficar em internal/?
Sim, para análise de ambiente e conexão de segredos.
Exponha apenas structs de configuração tipadas necessárias para testes.
E api/ vs internal/api/?
Se os manipuladores HTTP não forem superfícies de importação públicas, mantenha-os internos.
O SDK público pertence a pkg/ ou a um módulo cliente dedicado.
Como organizar um CLI com subcomandos?
cmd/tool/main.go delega para internal/cli usando cobra ou flag.
Cada subcomando pode ser um arquivo, não necessariamente um binário separado.
O caminho do go mod init afeta o layout?
O caminho do módulo define os prefixos de importação.
Os nomes dos diretórios devem se alinhar com os caminhos de importação para clareza.
Versões das Pilhas: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verifique o patch na compilação) , chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Y29kZWd1aWRlcy5pb3xjZ2lvNTE0fDIwMjYwNw==