Сервер и клиент генерируются из .proto. Ручные изменения сгенерированных файлов теряются при следующей генерации, поэтому контракт и бизнес-логика разделены.
Инструменты
Нужны protoc и два плагина:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Пример генерации:
protoc --go_out=. --go-grpc_out=. proto/orders/v1/orders.proto
protoc-gen-go создаёт сообщения, protoc-gen-go-grpc создаёт клиент и серверный интерфейс.
Один контракт порождает и типы сообщений, и код вызовов. Бизнес-логику пишут отдельно от сгенерированных файлов.
buf
buf упрощает настройку и проверки:
version: v2
generate:
plugins:
- name: go
out: gen/go
- name: go-grpc
out: gen/go
Запуск buf generate повторяет одну и ту же генерацию для команды. buf lint проверяет оформление, а buf breaking ловит несовместимые изменения контракта.
Что менять руками
Сервер реализует сгенерированный интерфейс и при необходимости встраивает UnimplementedOrderServiceServer. Клиент получает OrderServiceClient. Эти типы не редактируют.
Файлы .proto, настройки генерации и маппинги версий пакетов хранят в репозитории. Сгенерированный код либо коммитят для воспроизводимого чтения, либо генерируют в CI, но выбирают один подход.
Версии и пакет
Плагины фиксируют в go.mod или lock-файле инструментов. Иначе latest на двух машинах даст разные версии генератора и большие необъяснимые diff-файлы. Обновление генератора делают отдельным изменением и проверяют получившийся код тестами.
Go-пакет задают в самом контракте:
option go_package = "example.com/orders/gen/ordersv1";
После генерации путь импорта единый для сервера и клиента. Если go_package не задан или конфликтует с out, файлы окажутся не там, где их ищет приложение. Поэтому генерацию запускают повторно на чистом checkout и проверяют, что рабочее дерево осталось без изменений: это ловит и старую версию .proto, и случайную ручную правку.
Коротко
- Контракт в
.proto, бизнес-логика в обычных Go-файлах. protoc-gen-goотвечает за сообщения,protoc-gen-go-grpcза вызовы.buf lintиbuf breakingпроверяют стиль и совместимость.- Сгенерированный код не правят вручную.