A arquitetura de microsserviços tornou-se o padrão da indústria para aplicações de grande escala, permitindo que times trabalhem de forma independente e que componentes individuais escalem conforme a demanda. A linguagem Go (Golang), criada pelo Google, destaca-se nesse cenário devido ao seu baixo consumo de memória, concorrência nativa (goroutines) e velocidade de execução próxima ao C.
Neste artigo, vamos detalhar como organizar e estruturar microsserviços em Go utilizando contêineres Docker.
1. Organização do Repositório (Estrutura de Pastas)
Seguir uma estrutura limpa e padronizada é essencial para que o projeto continue legível à medida que cresce. O padrão recomendado para projetos Go é o seguinte:
/meu-microsservico
├── cmd/
│ └── app/
│ └── main.go # Ponto de entrada do sistema
├── internal/
│ ├── handler/ # Controladores HTTP ou gRPC
│ ├── model/ # Definição de entidades e regras de domínio
│ └── repository/ # Comunicação direta com banco de dados
├── pkg/ # Códigos compartilhados (utilitários)
├── Dockerfile # Configuração do contêiner
└── go.mod # Gerenciador de módulos do Go
2. Escrevendo o Dockerfile Otimizado (Multi-stage Build)
Para microsserviços, o tamanho da imagem Docker afeta diretamente o tempo de deploy (CI/CD) e o consumo de disco no cluster. Em Go, podemos utilizar o multi-stage build para gerar imagens finais de apenas alguns megabytes.
Exemplo de Dockerfile profissional:
# Estágio de Compilação
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main ./cmd/app
# Estágio Executável Final (Imagem Limpa)
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
Com essa estratégia, removemos o compilador Go e todo o histórico do código-fonte da imagem final, deixando apenas o binário compilado rodando sobre uma base Alpine extremamente leve e segura.
3. Subir e conferir o binário
O main.go em cmd/app só precisa responder em /health para o primeiro deploy. O restante da API entra nos pacotes de internal/.
package main
import "net/http"
func main() {
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
O Compose publica a porta só em localhost. A imagem final é a do Dockerfile da seção 2, sem o compilador.
services:
app:
build: .
ports:
- "127.0.0.1:8080:8080"
docker compose up -d --build
curl -fsS http://127.0.0.1:8080/health
docker image ls
A resposta ok é o processo no ar. docker image ls mostra a imagem final, sem a tag builder. Se o build falhar em go.sum, rode go mod tidy na máquina de desenvolvimento e versione o arquivo junto com o go.mod.
Conclusão
Combinar o alto desempenho nativo do Go com o isolamento de ambientes proporcionado pelo Docker é a fundação ideal para criar sistemas escaláveis e prontos para rodar em clusters Kubernetes. Essa arquitetura garante baixos tempos de resposta e uso eficiente de recursos de CPU e RAM no servidor.