gRPC Practices
Goals
- Keep transport concerns isolated from domain logic.
- Make gRPC services easy to test without starting a real server.
Guidance
- GRPC service impls are translators: unmarshal request, call domain service, marshal response.
- Keep all business decisions in injected
XXXManager or domain XXXHandler dependencies, not in the GRPC service.go itself.
- Convert domain errors to gRPC status codes at the handler boundary in one centralized place.
- Use
context.Context from the incoming RPC for cancellation and deadline propagation; do not create detached contexts.
- When handlers accumulate enough complexity, extract them into a dedicated package alongside the server definition.
- Let server initialization & GRPC service impl to be different packages: server package focus on dependency injection & initialization, service package focus on request / response translation.
- Register services through a constructor that accepts domain dependencies explicitly; avoid global state or init-time registration.
Structuring a gRPC Package
server.go: Server constructor, listener setup, graceful shutdown.
service.go: Handler methods implementing the generated interface.
- Keep proto-generated code in its own module or a
proto/ subdirectory; do not mix generated and hand-written code.
Review Bullets
- Does each handler method fit the pattern: unmarshal → delegate → marshal?
- Is business logic free of any gRPC-specific types (
codes, status, proto messages)?
- Is error-to-status conversion centralized rather than scattered across handlers?
- Are domain dependencies injected via the constructor, not accessed through globals?
- Is
context.Context from the RPC propagated to downstream calls?
Sources & References