O que é um monorepositório e por que ele é necessário em Go?
Monorepositório (monorepo) é uma abordagem para gerenciamento de código onde todos os projetos, bibliotecas e serviços são armazenados em um único repositório. No ecossistema Go, essa abordagem se torna especialmente popular devido ao suporte integrado a módulos e workspaces.
Para desenvolvedores iniciantes, o monorepositório oferece várias vantagens principais:
- Versão única de código — todas as dependências são sincronizadas
- Refatoração simplificada — as alterações são imediatamente visíveis em todos os projetos
- Padronização — configurações idênticas de linters, CI/CD e testes
- Transparência — fácil entender como diferentes partes do sistema estão relacionadas
Neste artigo, vamos abordar como organizar corretamente um monorepositório em Go, começando pela estrutura básica e terminando com exemplos práticos.
Estrutura básica de um monorepositório em Go
A organização correta do código é a base de um monorepositório bem-sucedido. Vamos considerar uma estrutura típica:
my-monorepo/├── apps/│ ├── web-api/ # Aplicação principal│ │ ├── main.go│ │ ├── go.mod│ │ └── internal/│ └── worker/ # Processador em segundo plano│ ├── main.go│ └── go.mod├── libs/│ ├── config/ # Configuração compartilhada│ │ ├── config.go│ │ └── go.mod│ └── logger/ # Logging│ ├── logger.go│ └── go.mod├── scripts/ # Scripts auxiliares├── docs/ # Documentação├── go.work # Go workspace└── README.mdPrincípios-chave desta estrutura:
- apps/ — aplicações executáveis (binários)
- libs/ — bibliotecas reutilizáveis
- scripts/ — automação e utilitários
- docs/ — documentação do projeto
Configuração do Go Workspace para o monorepositório
A partir do Go 1.18, foi introduzido o suporte oficial a workspaces através do arquivo go.work. Isso permite trabalhar com vários módulos simultaneamente sem a necessidade de publicá-los em um repositório remoto.
Vamos criar um workspace para nosso monorepositório:
// go.workgo 1.22
use ( ./apps/web-api ./apps/worker ./libs/config ./libs/logger)Após criar o go.work, todos os módulos da lista use ficarão acessíveis entre si localmente. Isso é especialmente conveniente durante o desenvolvimento — as alterações na biblioteca são imediatamente refletidas nas aplicações.
Inicialização dos módulos
Para cada componente, é necessário criar seu próprio go.mod. Exemplo para a biblioteca de configuração:
// libs/config/go.modmodule github.com/your-org/libs/config
go 1.22E para a aplicação web-api:
// apps/web-api/go.modmodule github.com/your-org/apps/web-api
go 1.22
require github.com/your-org/libs/config v0.0.0
replace github.com/your-org/libs/config => ../../libs/configObserve o replace — ele instrui o Go a usar a versão local da biblioteca, em vez de procurá-la na internet.
Exemplo de código: biblioteca e aplicação
Vamos criar uma biblioteca simples para configuração e uma aplicação que a utiliza.
Biblioteca libs/config
// libs/config/config.gopackage config
import ( "fmt" "os")
type Config struct { Port string Env string}
func Load() (*Config, error) { port := os.Getenv("PORT") if port == "" { port = "8080" }
env := os.Getenv("ENV") if env == "" { env = "development" }
return &Config{ Port: port, Env: env, }, nil}
func (c *Config) String() string { return fmt.Sprintf("Config{