Dos Requisitos às Especificações de Serviço Go
O Produto entrega histórias de usuário.
Busque em todas as páginas da documentação
O Produto entrega histórias de usuário.
A Engenharia precisa de contratos de handler, formatos de armazenamento e SLOs mensuráveis antes que alguém abra o main.go.
Este fluxo de trabalho transforma requisitos ambíguos em uma especificação de serviço Go que os stakeholders podem aprovar e os ICs podem implementar sem retrabalho.
Uma especificação completa de serviço Go nomeia superfícies HTTP ou gRPC, persistência, requisitos não funcionais e contratos de erro.
Histórias fornecem a voz do cliente; a especificação fornece a verdade testável.
Revise a especificação com o produto e os consumidores antes do compromisso do sprint.
Itere no documento compartilhado; gere código a partir de OpenAPI ou protobuf somente após as assinaturas estabilizarem.
Esqueleto mínimo de especificação para um novo endpoint HTTP em chi.
## Funcionalidade: Exportação de PDF de Fatura
### API
- `GET /v1/invoices/{id}/pdf` → 200 `application/pdf`
- Erros: 403 `invoice_unpaid`, 404 `invoice_not_found`, 500 `render_failed`
### Dados
- Ler tabela `invoices` (Postgres); sem novas colunas
- Bytes de PDF efêmeros; chave de cache S3 opcional `pdf/{invoice_id}`
### NFRs
- Latência p99 2s @ 100 RPS (staging)
- Idempotente: GET repetido retorna os mesmos bytes para a mesma revisão da fatura
- Autenticação: escopo JWT `billing:read`
### Fora do escopo
- Entrega por e-mail, exportação em loteQuando usar isso:
.protoTrecho da especificação mais assinatura de handler Go correspondente e mapeamento de erros.
// internal/api/export.go
package api
import (
"context"
"errors"
"net/http"
"github.com/go-chi/chi/v5"
)
var (
ErrInvoiceUnpaid = errors.New("invoice_unpaid")
ErrInvoiceNotFound = errors.New("invoice_not_found")
ErrRenderFailed = errors.New("render_failed")
)
type InvoiceReader interface {
GetInvoice(ctx context.Context, id string) (Invoice, error)
}
type PDFRenderer interface {
RenderInvoicePDF(ctx context.Context, inv Invoice) ([]byte, error)
}
func MountExport(r chi.Router, inv InvoiceReader, pdf PDFRenderer) {
r.Get("/v1/invoices/{id}/pdf", func(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
inv, err := inv.GetInvoice(r.Context(), id)
if err != nil {
writeErr(w, mapErr(err))
return
}
if inv.Status != "paid" {
writeErr(w, http.StatusForbidden, ErrInvoiceUnpaid)
return
}
bytes, err := pdf.RenderInvoicePDF(r.Context(), inv)
if err != nil {
writeErr(w, http.StatusInternalServerError, ErrRenderFailed)
return
}
w.Header().Set("Content-Type", "application/pdf")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bytes)
})
}# api/openapi.yaml (trecho)
paths:
/v1/invoices/{id}/pdf:
get:
operationId: getInvoicePdf
responses:
"200":
content:
application/pdf:
schema: { type: string, format: binary }
"403":
content:
application/json:
schema:
properties:
code: { enum: [invoice_unpaid] }O que isso demonstra:
InvoiceReader, PDFRenderer) seguem o empacotamento handler-service-repositoryerr.Error()| Seção da especificação | Entrada do Produto | Saída da Engenharia |
|---|---|---|
| API | Jornadas do usuário, telas | Caminhos, métodos, cargas úteis, códigos de status |
| Dados | Entidades nomeadas nas histórias | Tabelas, eventos, retenção, classe PII |
| NFRs | "Rápido", "seguro", "confiável" | p99, RPS, autorização, RTO/RPO, auditoria |
| Erros | Casos extremos na UX | Códigos estáveis, política de retentativa, macros de suporte |
| Escopo | MVP vs. posterior | Lista explícita de fora do escopo |
Loop de refinamento da história
Variante gRPC
Substitua os caminhos OpenAPI por RPCs .proto, campos de mensagem e detalhes de google.rpc.Status.
Mantenha as mesmas seções de NFR e dados.
Stubs Go gerados ficam em gen/ ou em um módulo versionado.
| SLI | Alvo | Medição |
|---|---|---|
| Disponibilidade | 99,9% mensal | Taxa de 5xx excluindo erros do cliente |
| Latência | p99 ≤ 2s | Histograma http_server_duration |
| Correção | 0 exportações mal faturadas | Diferença do trabalho de reconciliação |
// Prefira context em toda fronteira de I/O - a especificação deve indicar timeouts.
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()context que correspondam aos orçamentos de latência de NFR.go generate para oci-codegen ou buf generate na seção de build.Error(). Correção: A especificação exige um campo code estável; mapeie na camada do handler.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| OpenAPI-first | Serviços HTTP, múltiplos consumidores | gRPC apenas interno com buf já padrão |
| Protobuf-first | gRPC, pipeline de codegen forte | CRUD simples com um cliente web |
| Especificação orientada por exemplo | Pequeno CLI ou biblioteca | HTTP inter-equipes com revisão legal |
| Apenas ADR | Equipe única com API externa inexistente | Contratos voltados para o cliente que exigem alinhamento de UX |
Duas a quatro páginas por épico.
O suficiente para tabelas de API, diferenças de dados, NFRs e erros.
A implementação detalhada pertence a documentos de design vinculados a partir da especificação.
O Produto aprova o escopo e os critérios de aceitação.
A Engenharia aprova a viabilidade.
As equipes consumidoras aprovam alterações que quebram compatibilidade.
Inclua modelos lógicos e notas de migração.
O DDL completo pode ficar em um arquivo de migração vinculado, referenciado por versão.
Documente o gatilho de enfileiramento, o esquema da carga útil, a política de retentativa, o comportamento de dead-letter e a API de status visível para o usuário.
Nomeie a chave da flag, padrão desligado/ligado por ambiente e o proprietário do kill-switch na seção NFR.
Spikes respondem a desconhecidos; eles produzem uma seção de especificação revisada, não um substituto para NFRs.
A especificação lista o caminho do módulo e a versão mínima; observe alterações que quebram compatibilidade para serviços downstream.
SLOs voltados para o cliente na especificação.
Runbooks e limites de alerta são vinculados a partir da seção NFR da especificação.
Incremente spec_version em qualquer alteração de contrato.
Consumidores assinam entradas de changelog nas notas de lançamento.
Use rascunhos para velocidade, mas a revisão humana para erros, autorização e retenção de dados é obrigatória.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC 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).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026