Monorepositório com Go: organização de código na prática
\n\nMonorepositório (monorepo) é uma abordagem para gerenciamento de código-fonte onde todos os projetos, bibliotecas e serviços são armazenados em um único repositório. No mundo Go, essa abordagem se tornou especialmente popular devido ao suporte nativo a módulos e ao sistema flexível de pacotes. Neste artigo, vamos explorar como organizar um monorepositório em Go na prática, analisar uma estrutura típica, exemplos de código e melhores práticas.
\n\nPor que um monorepositório em Go?
\n\nO monorepositório resolve várias tarefas principais:
\n- \n
- Simplificação do gerenciamento de dependências — todos os pacotes internos estão em um só lugar, facilitando atualizações e refatorações. \n
- Histórico unificado de alterações — commits atômicos que afetam vários serviços funcionam sem sincronizações extras. \n
- Aceleração do CI/CD — é possível configurar pipelines que compilam apenas as partes alteradas do projeto. \n
- Melhoria da qualidade do código — é mais fácil implementar padrões unificados, linters e code reviews. \n
Estrutura do monorepositório Go: esquema básico
\n\nVamos analisar uma estrutura típica de monorepositório em Go. Como exemplo, considere um projeto composto por dois microsserviços (api e worker) e uma biblioteca compartilhada lib:
my-monorepo/\n├── go.work\n├── go.work.sum\n├── apps/\n│ ├── api/\n│ │ ├── go.mod\n│ │ ├── main.go\n│ │ └── handler.go\n│ └── worker/\n│ ├── go.mod\n│ ├── main.go\n│ └── process.go\n├── lib/\n│ ├── go.mod\n│ ├── auth/\n│ │ └── auth.go\n│ └── db/\n│ └── db.go\n├── scripts/\n│ ├── build.sh\n│ └── test.sh\n└── Makefile\n\nElementos principais:
\n- \n
go.work— arquivo de workspace do Go (Workspace) que conecta todos os módulos. \n apps/— diretório com aplicações (microsserviços, utilitários CLI, etc.). \n lib/— bibliotecas compartilhadas usadas por todas as aplicações. \n scripts/— scripts auxiliares para compilação, teste e deploy. \n
Configuração do Go Workspace (go.work)
\n\nGo 1.18+ fornece o mecanismo workspace, que permite trabalhar com vários módulos simultaneamente. Vamos criar o arquivo go.work na raiz:
go 1.22\n\nuse (\n ./apps/api\n ./apps/worker\n ./lib\n)\n\nAgora, durante o desenvolvimento, é possível executar comandos a partir da raiz do projeto, e o Go enxergará todos os módulos como um todo. Por exemplo, para executar os testes de todos os módulos:
\n\ngo test ./...\n\nE para compilar um serviço específico:
\n\ncd apps/api && go build -o bin/api .\n\nExemplo de código: biblioteca compartilhada e microsserviço
\n\nVamos criar uma biblioteca simples de autenticação em lib/auth/auth.go:
package auth\n\nimport "errors"\n\nvar (\n ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken verifica o token e retorna o ID do usuário.\nfunc ValidateToken(token string) (string, error) {\n if token == "" {\n return "", ErrInvalidToken\n }\n // Aqui pode haver uma verificação real de JWT ou chamada a um serviço externo\n return "user-123", nil\n}\n\nAgora, vamos usar esta biblioteca no microsserviço apps/api/handler.go:
package main\n\nimport (\n "encoding/json"\n "net/http"\n "my-monorepo/lib/auth"\n)\n\ntype UserResponse struct {\n UserID string `json:"user_id"`\n}\n\nfunc UserHandler(w http.ResponseWriter, r *http.Request) {\n token := r.Header.Get("Authorization")\n userID, err := auth.ValidateToken(token)\n if err != nil {\n http.Error(w, "Unauthorized", http.StatusUnauthorized)\n return\n }\n\n resp := UserResponse{UserID: user