Is it possible to split service implementaion?
filanov opened this issue · comments
I have a service that support quite a lot of apis, i would like to know if i can split the server side into multiple interfaces
Client side should remain a single interface but for the service i would like to have a separation.
In the past when working with REST API and go-swagger i could add a tag specifying to which interface each call will be related to, is that possible in grpc?
an example:
proto:
service MyServer {
rpc Dog(SomeDog) returns (SomeDog);
rpc Cat(SomeCat) returns (SomeCat);
}
generated client
type MyServerClient interface {
Dog(ctx context.Context, in *SomeDog) (*SomeDog, error)
Cat(ctx context.Context, in *SomeCat) (*SomeCat, error)
}
generated server
type MyServerServer interface {
Dog(ctx context.Context, in *SomeDog) (*SomeDog, error)
Cat(ctx context.Context, in *SomeCat) (*SomeCat, error)
}
what i want:
type MyServerDogServer interface {
Dog(ctx context.Context, in *SomeDog) (*SomeDog, error)
}
type MyServerCatServer interface {
Cat(ctx context.Context, in *SomeCat) (*SomeCat, error)
}
Why would i want it?
- Reduce complexity, Split the logic of the service to smaller contained parts
- Easy to test if there are dependencies between the apis, lets say services implement A,B,C and a is calling for B and C but doesn't need to know all the logic in order to write a unit tests, i would like to be able to mock B and C
- Still have a single client for the service
It sounds like you could do something like this yourself by splitting up the service manually, then on the client side, do something like:
type MyServerClient struct {
MyServerDogClient
MyServerCatClient
}
...
func main() {
cc, err := grpc.NewClient(target, ...) // handle err
c := MyServerClient{
MyServerDogClient: pb.NewServerDogClient(cc),
MyServerCatClient: pb.NewServerCatClient(cc),
}
c.Dog()
c.Cat()
...
And if you wanted you could make a helper and share it for your users as a convenience:
func NewMyServerClient(cc grpc.ClientConnInterface) *MyServerClient {
return &MyServerClient{
MyServerDogClient: pb.NewServerDogClient(cc),
MyServerCatClient: pb.NewServerCatClient(cc),
}
}
It's fairly common to have services with a small number of methods, and then serve multiple services from a single server.
the client is external and not necessary a go client
In that case, other languages would have to find something ergonomic, but there is probably something.
As I said, it's pretty common to have very small services with just one or a few methods, and for servers to serve many services.