Design de Esquemas de Protocol Buffers
Esquemas Protobuf são contratos vivos.
Busque em todas as páginas da documentação
Esquemas Protobuf são contratos vivos.
Números de campo, tipos de fio e limites de pacote decidem se você pode implantar um novo serviço sem derrubar metade da frota.
Mensagens Protobuf são estruturas de dados versionadas definidas em arquivos .proto.
Cada campo tem uma tag numérica permanente.
Clientes e servidores compilados com meses de diferença ainda devem ser capazes de decodificar as cargas úteis uns dos outros.
O design do esquema é design de API: nomes importam para legibilidade, mas números importam para compatibilidade.
Esta página cobre layout de mensagens, oneof, mapas, enums, importações e as regras que o Google documenta para evolução segura.
Cartão de receita de referência rápida - pronto para copiar e colar.
syntax = "proto3";
package inventory.v1;
option go_package = "example.com/inventory/api/inventory/v1;inventoryv1";
import "google/protobuf/timestamp.proto";
message Item {
string id = 1;
string sku = 2;
int32 quantity = 3;
google.protobuf.Timestamp updated_at = 4;
oneof status {
bool in_stock = 5;
string backorder_note = 6;
}
map<string, string> attributes = 7;
}Quando usar isso:
oneof em vez de flags booleanas paralelas.map.Timestamp, Duration, Empty) em vez de reinventá-los.syntax = "proto3";
package orders.v1;
option go_package = "example.com/orders/api/orders/v1;ordersv1";
import "google/protobuf/field_mask.proto";
enum OrderState {
ORDER_STATE_UNSPECIFIED = 0;
ORDER_STATE_PENDING = 1;
ORDER_STATE_SHIPPED = 2;
}
message Order {
string id = 1;
OrderState state = 2;
repeated LineItem items = 3;
}
message LineItem {
string product_id = 1;
int32 qty = 2;
}
message UpdateOrderRequest {
Order order = 1;
google.protobuf.FieldMask update_mask = 2;
}package main
import (
"fmt"
ordersv1 "example.com/orders/api/orders/v1"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
func main() {
req := &ordersv1.UpdateOrderRequest{
Order: &ordersv1.Order{
Id: "ord-1",
State: ordersv1.OrderState_ORDER_STATE_SHIPPED,
},
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"state"}},
}
fmt.Println(req.GetUpdateMask().GetPaths())
}O que isso demonstra:
UNSPECIFIED - nunca use zero como um estado de negócio significativo sem documentá-lo.repeated mapeia para slices Go; campos ausentes decodificam como nil/vazio, não erros.FieldMask expressa atualizações parciais para RPCs no estilo PATCH.go_package mantém o código Go gerado em um caminho de importação estável.Protobuf codifica mensagens como trios de tag-comprimento-valor no fio.
O número do campo seleciona o slot; o tipo de fio informa aos decodificadores como analisar os bytes.
Decodificadores ignoram campos desconhecidos, que é a base da compatibilidade retroativa.
Renomear um campo em .proto não altera o formato do fio - apenas o número e o tipo o fazem.
| Regra | Mudança segura | Mudança que quebra |
|---|---|---|
| Adicionar campo opcional | Sim, com novo número | Reutilizar um número antigo |
| Alterar tipo de campo | Não | int32 para string na mesma tag |
| Renomear campo | Sim (mesmo número) | - |
| Excluir campo | Reservar número + nome | Reutilizar tag excluída |
Palavra-chave optional | Presença explícita em proto3 | - |
Use reserved 3, 5; e reserved "legacy_field"; após remoções.
oneof, mapas e mensagens aninhadasoneof impõe exclusão mútua - apenas um membro está definido.
Em Go, wrappers gerados expõem acessadores no estilo GetInStock() e um tipo de interface para o caso ativo.
Campos map<K,V> se tornam mapas Go; chaves não podem ser mensagens ou floats.
Mantenha valores de mapa como strings ou números simples quando possível para consistência entre linguagens.
Aninhe mensagens para clareza, mas achate quando a mesma estrutura se repete entre RPCs - tipos compartilhados pertencem a common.proto.
// Prefira construtores para invariantes obrigatórios
order := &ordersv1.Order{
Id: id,
State: ordersv1.OrderState_ORDER_STATE_PENDING,
}
// Clone antes de mutar protobufs compartilhados passados entre goroutines
clone := proto.Clone(order).(*ordersv1.Order)proto.Marshal / proto.Unmarshal funcionam sem gRPC para cargas úteis de eventos.buf lint ou prototool em CI para impor estilo e detecção de quebra de compatibilidade.int32 para enum - Tipos de fio podem corresponder, mas semânticas divergem. Correção: adicione um novo número de campo para o enum.PENDING colidem. Correção: mantenha UNSPECIFIED = 0 e comece os valores de negócio em 1.oneof enorme com muitos braços - O código Go gerado incha. Correção: divida variantes em mensagens aninhadas ou RPCs separadas..proto. Correção: gere OpenAPI ou use verificações de quebra buf contra o main.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Mensagens protobuf simples | Serviços gRPC, eventos | Você precisa de blobs sem esquema e sem tipo |
google.protobuf.Struct | Atributos verdadeiramente dinâmicos | Caminhos críticos de desempenho |
| FlatBuffers / Cap'n Proto | Cargas de trabalho de leitura com zero cópia | Equipe já padronizada em gRPC+proto |
| JSON Schema + OpenAPI | APIs HTTP-only públicas | Você quer eficiência binária e geração de código |
| Avro com schema registry | Pipelines centrados em Kafka | gRPC simples ponto a ponto |
A codificação do fio usa apenas números.
Renomear é seguro; renumerar não é.
Proto3 é o padrão para novos trabalhos gRPC.
Proto2 persiste em APIs legadas do Google; evite misturar em um único serviço.
Ele rastreia a presença explícita separadamente dos valores zero padrão.
Útil para semântica PATCH e para distinguir "não definido" de "string vazia".
Extraia mensagens comuns para common/v1/types.proto e importe-as.
Mantenha os nomes dos pacotes versionados (v1, v2).
Use google.protobuf.Timestamp em protos.
Converta nas fronteiras com timestamppb.New(t) em Go.
Mantenha as cargas úteis de RPC abaixo de alguns MB; use streaming ou armazenamento de objetos para grandes volumes binários.
Mensagens enormes prejudicam a latência e os buffers do proxy.
buf breaking, prototool break check, ou CI fixada contra uma referência git base.
Gateways mapeiam enums para strings em JSON.
Nomes de enum Proto devem ser estáveis, pois aparecem externamente.
Use comentários // acima dos campos; alguns geradores emitem comentários no estilo godoc.
Para APIs públicas, espelhe as documentações no README do seu repositório de esquema.
Proto3 omite valores padrão na codificação binária.
Não dependa de receber valores zero para campos primitivos não definidos, a menos que esteja usando optional.
Versões da Stack: Esta página foi escrita para Go 1.26.x (GC padrão 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: 18 de jul. de 2026