What's the most intuitive way to organize a set of endpoints to fetch users and their roles in groups? i.e.
- Users and their roles in a specified group.
- Groups a user is in, and their role in each group.
Attempt 1
We began with the following:
message UserWithRole {
User user = 1;
Role role = 2;
}
message GetUsersInGroupRequest {
string group_id = 1;
}
message GetUsersInGroupResponse {
repeated UserWithRole users = 1;
}
rpc getUsersInGroup (GetUsersInGroupRequest) returns (GetUsersInGroupResponse)
And for the flip side (groups a user belongs to):
message GroupWithRole {
Group group = 1;
Role role = 2;
}
message GetGroupsForUserRequest {
string user_id = 1;
}
message GetGroupsForUserResponse {
repeated GroupWithRole groups = 1;
}
rpc getGroupsForUser (GetGroupsForUserRequest) returns (GetGroupsForUserResponse)
This felt awkward and unnecessarily verbose.
Attempt 2
So, what about a using a single model?
message UserGroupMapping {
User user = 1;
Group group = 2;
Role role = 3;
}
message GetUsersInGroupRequest {
string group_id = 1;
}
message GetUsersInGroupResponse {
repeated UserGroupMapping user_group_mappings = 1;
}
rpc getUsersInGroup (GetUsersInGroupRequest) returns (GetUsersInGroupResponse)
message GetGroupsForUserRequest {
string user_id = 1;
}
message GetGroupsForUserResponse {
repeated UserGroupMapping user_group_mappings = 1;
}
rpc getGroupsForUser (GetGroupsForUserRequest) returns (GetGroupsForUserResponse)
This appeals because the same model can be used for other endpoints that return both a user and group:
rpc getUserAndPrimaryGroup (string user_id) returns (UserGroupMapping);
Let's even say we consolidate the above like this:
Attempt 2b
message UserGroupMapping {
User user = 1;
Group group = 2;
Role role = 3;
}
message GetUserGroupMappingsRequest {
enum IdType {
USER_ID = 0;
GROUP_ID = 1;
}
IdType id_type = 1;
string id = 2;
}
message GetUserGroupMappingsResponse {
repeated UserGroupMapping user_group_mappings = 1;
}
rpc getUserGroupMappings (GetUserGroupMappingsRequest) returns (GetUserGroupMappingsResponse)
But either way, the problem is that one of the dimensions, User
or Group
is always needlessly repeated despite being the uniform for any given request, _i.e. if you're getting a user's groups, there's no need to repeat User
again and again, but here we are.
Summary
I know either way "works," but since this will be a very central microservice (used by other microservices managing resources, payments, auditing, etc.) I want to design the API right from the start.
Meanwhile I'll continue searching the web for protobuf user group management api design examples etc. but just getting a lot of irrelevant results.