[Proposal] Naming
Naming
- State: Draft
- Replaced by:
- Created:
- Tags:
Context
The two primary options are:
ActionResource(for example,CreateBook)ResourceAction(for example,BookCreate)
With ResourceAction:
AccountCreate
AccountDelete
AccountGet
AccountSuspend
ApiTokenCreate
ApiTokenRefresh
ApiTokenRevoke
With ActionResource:
CreateAccount
CreateApiToken
DeleteAccount
GetAccount
RefreshApiToken
RevokeApiToken
SuspendAccount
Resolution
...
Would this be for GraphQL, gRPC, JSON API? Regardless, my first immediate take is that, after so many years, I prefer to do ResourceAction so that things naturally stick to the proper grouping.
This reminds an issue from 2017 😭 https://github.com/graphql/graphql-spec/issues/376
Would this be for GraphQL, gRPC, JSON API? Regardless, my first immediate take is that, after so many years, I prefer to do
ResourceActionso that things naturally stick to the proper grouping.This reminds an issue from 2017 😭 graphql/graphql-spec#376
Yeah, alphabetical sorting is handy for grouping API methods.
We could also use resource-specific services to handle that naturally. For example, resource-specific AdUnitService vs broader domain-based ChatService.
I haven't worked with GraphQL or JSON API, so can't really weigh in there 😭
With resource-specific services:
iam.example.com/
├── AccountService/
│ ├── create_account()
│ ├── delete_account()
│ ├── get_account()
│ └── suspend_account()
└── ApiTokenService/
├── create_api_token()
├── refresh_api_token()
└── revoke_api_token()
This is a tricky subject, before we even get to the naming, we would need to agree on what "Resource" actually means AND after that what "Service" means.
It is not that obvious based on my experience.
Especially when Phyisical Boundaries and Logical Boundaries are ignored.
Lastly, I am trying to avoid creating yet another spec; so we are leaning on Google AIP as much as we can.
Sometimes, the names for request messages can become a little weird, for example SearchSearchAds360StreamRequest
The messages can also have super-duper-long names, for example VideoActionRecognitionPredictionInstance (this one is used indirectly though, under PredictRequest)
I do not like the fact that they aren't following something obvious: https://github.com/googleapis/googleapis/blob/3acfdb6166a7101691cdf812bf8f8a67a2084fde/google/ads/searchads360/v0/services/search_ads360_service.proto#L111-L129
Like [service name][rpc name][Request|Response] that is a very confusing name
That's the longest method name I've found -- UpdateSecurityHealthAnalyticsCustomModule
I do not like the fact that they aren't following something obvious: https://github.com/googleapis/googleapis/blob/3acfdb6166a7101691cdf812bf8f8a67a2084fde/google/ads/searchads360/v0/services/search_ads360_service.proto#L111-L129
Like
[service name][rpc name][Request|Response]that is a very confusing name
Seems relevant: https://github.com/aip-dev/google.aip.dev/issues/1085
Should be I feel adding Stream is a good idea, Streaming is long ... I do not see the point.
Some of those long names may be related to what I said before "Physical Boundaries and Logical Boundaries"
"Physically", you may want one port behind 1 service, but logically, that service has many resources ... so, you end up with such long naming.
You could create more "services" but may means multiple ports, sometimes, or other infra level stuff people dont want to deal with.
Assumptions. A lot of assumptions.
What I definitely do not support is doing Create or Update based on the service name, so always include the "resource" in the rpc, Create[x] or Update[x]
I propose to standardize the following terminology:
From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L50:
API Interface
The element of an API specification IDL that groups API methods, such as a Protocol Buffers
servicedefinition. It is typically mapped to a similar high level grouping mechanism in most programming languages, like aclassorinterface.
From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L76:
API Service
A deployed implementation of one or more APIs, exposed on one or more network addresses, such as the Cloud Pub/Sub API.
From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L87:
API Service Endpoint
Refers to a network address that an API service uses to handle incoming API requests. One API service may have multiple API service endpoints, such as
https://pubsub.googleapis.comandhttps://content-pubsub.googleapis.com.
Given the new terminology, I was suggesting that developers should be encouraged to create ~resource-specific services~ resource-scoped API interfaces so that things could be grouped naturally.
I'm not confident about this suggestion anymore though because it might bloat the boilerplate code.
Just imagine that you have some APIServiceEndpointGrpcClient. Such struct would have to store a single API Interface per field which might be cumbersome if we have tens or hundreds of other API interfaces sitting alongside.
The broader domain-based ChatService approach has an advantage in this context.
Yeah, we'll have a lot of irrelevant results when typing something like grpc_client.chat_service.c in the text editor but I assume the modern IDEs should be smart enough to convert grpc_client.chat_service.c into grpc_client.chat_service.create_ rather than into grpc_client.chat_service.create_{first_alphabetically_sorted_match} when there's multiple entries in results and all of them start with grpc_client.chat_service.create_
Given that, I think that resource-scoped API interfaces like AdUniService is a bad practice unless proven otherwise and it should be discouraged.
"Physically", you may want one port behind 1 service, but logically, that service has many resources ... so, you end up with such long naming.
You may host multiple API Interfaces under single API Service Endpoint which may have only one port. gRPC supports that but I'm not sure about other IDLs you mentioned. Shouldn't we discourage the use of any other IDLs rather than gRPC?
I am onboard with almost everything, my only curiosity it is around subdomain vs URL paths,
https://pubsub.googleapis.com and https://content-pubsub.googleapis.com
vs.
https://whatever.googleapis.com/googleapis.pubsub.v1.Service and https://whatever.googleapis.com/googleapis.content-pubsub.v1.Service
What exactly are you comparing? I'm not sure if I understand. But it seems like you also have to include the API Interface Method name to make the URI complete.
From https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests:
Path → ":path" "/" Service-Name "/" {method name} ; But see note below.Path is case-sensitive. Some gRPC implementations may allow the Path format shown above to be overridden, but this functionality is strongly discouraged. gRPC does not go out of its way to break users that are using this kind of override, but we do not actively support it, and some functionality (e.g., service config support) will not work when the path is not of the form shown above.
ooooh nevermind then! nothing burger from my end.
You can move the conversation to a PR, I think we are in alignment here, and ultimate, following Googles' AIP would be a decent route I think
I'm a bit hesitant about making a PR just yet. I have the feeling like something is missing.
Yeah, we'll have a lot of irrelevant results when typing something like grpc_client.chat_service.c in the text editor but I assume the modern IDEs should be smart enough to convert grpc_client.chat_service.c into grpc_client.chat_service.create_ rather than into grpc_client.chat_service.create_{first_alphabetically_sorted_match} when there's multiple entries in results and all of them start with grpc_client.chat_service.create_
Given that, I think that resource-scoped API interfaces like AdUniService is a bad practice unless proven otherwise and it should be discouraged.
For example, there's definitely more than a one way to represent automatically generated API Methods. Previously, I was using the pattern {grpc_client}.{api_service_name}.{method_name} but it's also possible to represent the methods as Rust traits which will result in even shorter boilerplate:
mod googleapis_codegen {
pub struct GrpcClient;
mod ads {
mod ad_manager {
pub trait AdUnitService {
fn get_ad_unit(&self);
}
impl AdUnitService for GrpcClient {
// sends a request to https://ads.googleapis.com/googleapis.ads.admanager.v1.AdUnitService/get_ad_unit
fn get_ad_unit(&self) {
...
}
}
}
}
}
use googleapis_codegen::GrpcClient;
use googleapis_codegen::ads::ad_manager::AdUnitService; // the name of the trait
grpc_client: GrpcClient;
// use the trait implicitly
grpc_client.get_ad_unit(...)
// use the trait explicitly
<grpc_client as AdUnitService>.get_ad_unit(...)
So it might be not reasonable to state that resource-scoped API interfaces is a bad practice. Further research is needed.
Do you have any thoughts on codegen for golang/elixir/whatever? Would be super good if we could use something as effective as Rust traits but for different languages.
In terms of gRPC, I tent to use the official code-gen, so they would create a directory structure based on the package name