protobuf with grpc for Go in split packages

Issue

I’m trying to make my Go project using hexagonal architecture as described here.
In my project I’m using a gRPC communication generated with protoc from .proto file.

The directories structure:

|- grpc.proto
|-internal
  |-core
    |-domain
  |-services
    |- grpcprotocol

And my grpc.proto file has go_package option that points to a specific directory in my Go project

syntax = "proto3";
option go_package = "github.com/myuser/myrepo/internal/core/services/grpcprotocol";
...

Using protoc --go_out=internal/core/domain --go_opt=paths=source_relative --go-grpc_out=internal/core/services/grpcprotocol --go-grpc_opt=paths=source_relative ports.proto I’m able to generate both grpc.pb.go file in internal/core/domain directory and grpc_grpc.pb.go file inside internal/core/services/grpcprotocol directory.

However, grpc.pb.go has a go package named grpcprotocol while it should have a package named domain (I also use other types defined there in separate Go files).
Also grpc_grpc.pb.go file has code that uses types defined in grpc.pb.go without imports (it treats it as if it was defined in the same package).

Is it possible to split those two files into separate Go packages and enforce code that’s in grpc_grpc.pb.go to import types from domain directory instead of treating them as defined in the same package?

Solution

Your best solution here is too separate the code that you want in grpcprotocol and the one you want into domain, into separate files. Such as:

domain.proto

syntax = "proto3";
package domain;
option go_package = "github.com/myuser/myrepo/internal/core/domain";

//...

grpc.proto

syntax = "proto3";
package grpcprotocol;
option go_package = "github.com/myuser/myrepo/internal/core/services/grpcprotocol";

//...

Then you could import your domain.proto in your grpc.proto, by simply writing import "domain.proto";, provide a --proto_path if domain.proto and grpc.proto are not in the same directory. And finally to reference an object from domain.proto in grpc.proto you can write:

domain.AnObject

After that you can take advantage of the --go_opt=module and --go-grpc_opt=module, to strip the module name in go_package and generate the code at the right place. Like:

protoc --go_out=. --go_opt=module=github.com/myuser/myrepo --go-grpc_out=. --go-grpc_opt=module=github.com/myuser/myrepo *.proto

What this will do is that, it will remove github.com/myuser/myrepo from each go_package and basically start from the root of your module. That’s why you can do a --go_out=. and --go-grpc_out=..

Hope that helps, let me know how I can further improve my answer.

Notes

  • Protobuf package and go_package are not the same. The former is only used for protobuf in order to give context and it extends the qualified name. The go_package is used during go code generation.
  • The package in the proto file is optional, it makes things more clear and nobody can misuse you proto file without specifying the fully qualified name (little bit safer, if named properly).

Answered By – Clément Jean

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.