grpc / grpc-go

The Go language implementation of gRPC. HTTP/2 based RPC

Home Page:https://grpc.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.