Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to split service implementaion? #7078

Closed
filanov opened this issue Apr 3, 2024 · 3 comments
Closed

Is it possible to split service implementaion? #7078

filanov opened this issue Apr 3, 2024 · 3 comments

Comments

@filanov
Copy link

filanov commented Apr 3, 2024

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
@dfawley
Copy link
Member

dfawley commented Apr 3, 2024

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.

@dfawley dfawley closed this as not planned Won't fix, can't repro, duplicate, stale Apr 3, 2024
@filanov
Copy link
Author

filanov commented Apr 4, 2024

the client is external and not necessary a go client

@dfawley
Copy link
Member

dfawley commented Apr 4, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants