Skeleton de API em Go — repository pattern, JWT, Redis, OpenTelemetry (Jaeger), queue worker, scheduler, eventos e rate limit. Arquitetura espelhada no Laravel-skeleton, sem frontend.
Base sólida para APIs RESTful em Go de médio porte, incluindo:
- ✅ Arquitetura em camadas — Handler → Service → Repository → Model
- ✅ Repository pattern com interfaces — troca de backend sem tocar na lógica de negócio
- ✅ JWT Auth — guards separados por rota, role-based (admin/user)
- ✅ Rate limit — por IP, configurável por rota
- ✅ Redis — cache + fila de jobs
- ✅ Queue worker — processo separado, handlers registráveis
- ✅ Scheduler — tarefas recorrentes em processo dedicado
- ✅ Events — pub/sub interno síncrono e assíncrono (fire-and-forget)
- ✅ Observabilidade — OpenTelemetry → Jaeger (liga com uma env)
- ✅ Testes unitários e de integração — mocks hand-rolled, sem dependências externas
- ✅ Ambiente dockerizado — app + worker + scheduler + MySQL + Redis + Jaeger prontos
/
├── docker-compose.yml # Orquestração dos serviços
├── Makefile # Atalhos para comandos comuns
├── .env.example # Variáveis de ambiente
├── docker/
│ ├── Dockerfile # Multi-stage build
│ └── otel-collector.yml # Config do OpenTelemetry collector
├── cmd/
│ ├── api/ main.go # Entrypoint da API
│ ├── worker/ main.go # Entrypoint do queue worker
│ ├── scheduler/ main.go # Entrypoint do scheduler
│ └── migrate/ main.go # Entrypoint das migrations
├── migrations/
│ ├── migrate.go # AutoMigrate de todos os models
│ └── seed.go # Seeders (dados iniciais)
└── internal/
├── config/ # Carrega variáveis de ambiente
├── router/ # Rotas — equivalente ao routes/api.php
├── middleware/ # Auth JWT, AdminOnly, RateLimit, CORS, Logger
├── events/ # Bus pub/sub interno
├── queue/ # Worker + Dispatch via Redis
├── scheduler/ # Tarefas recorrentes
├── infra/
│ ├── database/ # Conexão GORM + MySQL
│ ├── redis/ # Conexão + RedisService
│ ├── mailer/ # SMTP
│ └── tracer/ # OpenTelemetry
└── domain/
└── user/ # Exemplo de domínio completo
├── model/ # Struct GORM
├── repository/ # Interface + implementação GORM
├── service/ # Lógica de negócio + erros sentinel
└── handler/ # HTTP handlers (Controllers)
cp .env.example .envSuba os containers:
docker compose up -ddocker exec -it go-skeleton-app sh./migratePara popular o banco com dados iniciais, edite migrations/seed.go e chame migrations.Seed(db) no entrypoint de migrate.
Usuários criados por padrão:
| Senha | Role | |
|---|---|---|
| admin@example.com | password | admin |
| alice@example.com | password | user |
| Serviço | URL |
|---|---|
| API | http://localhost:8020/api/v1 |
| Health | http://localhost:8020/health |
| MySQL | localhost:3306 |
| Redis | localhost:6379 |
| Jaeger UI | http://localhost:16686 |
POST /api/v1/auth/login # Autenticação — retorna JWT
POST /api/v1/auth/register # Cadastro
GET /api/v1/users # Listar usuários [JWT]
GET /api/v1/users/:id # Buscar por ID [JWT]
POST /api/v1/users # Criar usuário [JWT]
PUT /api/v1/users/:id # Atualizar usuário [JWT]
DELETE /api/v1/users/:id # Deletar usuário [JWT + admin]
GET /health # Health check
# Subir tudo
make up
# Rodar a API localmente (sem Docker)
make run
# Rodar worker localmente
make worker
# Rodar scheduler localmente
make scheduler
# Rodar todos os testes
make test
# Testes com cobertura
make test-cover
# Lint (requer golangci-lint)
make lint
# Limpar dependências
make tidySiga o mesmo padrão do domínio user:
internal/domain/produto/
model/ produto.go # struct GORM
repository/ produto_repository.go # interface + impl
service/ produto_service.go # lógica de negócio
errors.go # erros sentinel
handler/ produto_handler.go # HTTP handlers
Registre no router em internal/router/router.go:
produtoRepo := repository.NewProdutoRepository(db)
produtoSvc := service.NewProdutoService(produtoRepo)
produtoHandler := handler.NewProdutoHandler(produtoSvc)
produtos := protected.Group("/produtos")
produtos.GET("", produtoHandler.List)
produtos.GET("/:id", produtoHandler.Show)
produtos.POST("", produtoHandler.Store)
produtos.PUT("/:id", produtoHandler.Update)
produtos.DELETE("/:id", middleware.AdminOnly(), produtoHandler.Destroy)Registre a migration em migrations/migrate.go:
db.AutoMigrate(&model.Produto{})queue.Dispatch(ctx, rdb, "send_welcome_email", map[string]string{
"email": user.Email,
"name": user.Name,
})Registre o handler no cmd/worker/main.go:
worker.Register("send_welcome_email", func(ctx context.Context, payload json.RawMessage) error {
// processar aqui
return nil
})events.DispatchAsync(events.UserCreated, map[string]any{"user_id": user.ID})Registre um listener em qualquer init() ou no bootstrap:
events.Listen(events.UserCreated, func(e events.Event) {
log.Printf("usuário criado: %v", e.Payload)
})Por padrão o Jaeger fica desligado. Para ativar:
OTEL_ENABLED=trueAcesse os traces em http://localhost:16686.
-
cp .env.example .env -
docker compose up -d -
docker exec -it go-skeleton-app /app/migrate - Testar
POST /api/v1/auth/logincomadmin@example.com/password
Pronto 🎉
Veja SECURITY.md.
MIT