-
Notifications
You must be signed in to change notification settings - Fork 66
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
[Proposal] Allow languages to customize package/namespace structure of generated proto APIs #169
Comments
You don't need to do anything to this repository to get the protos the way you want for your SDK. I have had many cases where I move generated classes around, rename them, etc harmlessly. This does not affect the source protos nor should it. Make your generated protos look however you want to look in your SDK. In fact, you probably should as the default protoc for most languages is ugly. If that means as part of your build script you take the protos and move them around the way your draft PR does, no problem. If that means advanced post processing, no problem. That doesn't need to affect this repository. We do some advanced proto generation post-processing in the Python SDK for example because the generated result is not to our liking. Having said that, I do support changing the |
Absolutely. However, Conversely, the between languages consistency, is -while nice - less important. Languages should follow their preferred design principles when creating namespaces. As an example, if one language has the wildcard ( |
Also, even if for some reason we decided to have a different value for |
I am not sure the difference between "ServiceApi" and "Api" is a language-specific concern.
This is not true though. It is actually easier for most languages' protoc generators if protos for separate proto packages are in separate directories. We had to refactor core protos at temporalio/sdk-core#255 to basically do the opposite of what you're doing here so that the same dir didn't have two of the same unqualified message name in it. The two reasons I gave in that issue:
Unfortunately protoc in each language leaves a lot to be desired and most languages have to adapt the result to their needs. I understand this makes it tough for .Net, but I don't think anything short of maybe |
Interesting point. I guess, the protoc compilers are quite different across languages. Perhaps the first proposed PR (#170) highlights a better direction (rather than the second like I thought initially). There, the proto files are kept in the same directories as today (unchanged), but the file-names are made unique. E.g.: |
Could you please clarify on this? Under what circumstances in this an issue? |
Sorry I just saw this. For proto writers, In general though, I think at least for now, having the .Net namespaces mirror the proto packages is reasonable (same as all of our other SDKs, though slight changes may be needed if protoc generates an ugly name for something). |
Hi folks,
I have a proposal (incl. a sample solution) that I would like to discuss. If folks find the following interesting in general, I have a draft PR, for which I'd love to collect feedback.
Here, I am making a critical assumption on which the need to this proposal is based. Let us assume that the assumption is, at least partially, correct. If there are strong concerns with it, we can get back to it later:
Here, I am proposing an improvement of that kind. 😃
Context
Our proto files specify, among other things, what package names (aka namespaces) the generated language APIs should have in various target languages. So far we have left them unchanged, simply following the directory structure in the API repo. For established languages this is now a done deal, as changing the package names is a compatibility break. For new / not-established languages, we may have some more flexibility to improve things (if needed).
Problem
While designing and developing the .NET SDK I have met the need to constantly import a multitude of
Temporal.Api.Xyz
namespaces. It feels quite redundant and annoying. Doing it once is not a big deal. But in practice, you have to do it for EVERY SINGLE FILE that uses Temporal's data contracts in your application. And in a non-trivial application, that can be quite many files. Java users may not fully appreciate this, as they can simply importTemporal.Api.*
, but such a wildcard syntax does not exist in all languages.Of course, the IDE can help doing this quickly. But just like with the
*
-syntax, the convenience of it varies across tools and environments. Some developers simply prefer using notepad-like editors. In other cases, the advanced IDEs' convenience factors for this particular scenario vary too. For example, Visual Studio does not automatically import namespaces for good reasons that go beyond the scope of this discussion. Instead, you have to click on a type that is not yet "found" (or hit a hot-key while the cursor is there) and then select an item in the appearing drop-down menu, and VS will then import the namespace. You have to do it every time a type is not found. In our case - once per required Temporal namespace in each file that needs that namespace. So basically - a lot.Solution
One of our guiding principles is focus on our users/developer. And we can make their lives better by improving on the package/namespace structure of temporal APIs. Crucially, we can only do it for new or almost new Temporal languages.
For example, for .NET, I would consider it a great story to reduce all the namespaces to the following four:
Explanation:
Root namespace:
Temporal.ServiceApi.V1....
From the perspective of the user, it is the service API. For us, of course, it is the API. But the user is faced with several Temporal APIs. At the very least, there is the SDK, but in some cases, it is structured further (the worker SDK, the activity completion client, command-line tools, ...). This particular API is just one of several. Specifically, the service-API.
The latter choice we took in early languages, but it seems strange. If we have a V2, who is to say that it will have the same structure? If we have the version name on the package name, it should perceed the specifics to allow a different organization in a potential V2.
Temporal.ServiceApi.V1.WorkflowService
The RPC declarations (and only the RPC declarations) for the workflow service. No data exchange types. Essentially, what is currently declared in workflowservice/v1/service.proto.
Temporal.ServiceApi.V1.OperatorService
The RPC declarations (and only the RPC declarations) for the operator service. No data exchange types. Essentially, what is currently declared in operatorservice/v1/service.proto.
Temporal.ServiceApi.V1.DataModel
All data exchange types. There is no reason for distinct namespaces. For the user, this is the data model for interacting with the Temporal service. Currently, this includes everything that is not in the other namespaces.
Temporal.ServiceApi.V1.DataModel.ErrorDetails
The contracts here are special because they are not really part of the API. In fact, to my knowledge, currently only Go, Java, Python, and C++ actually support the gRPC richer error model on which these are based.
Engineering considerations
I prototyped making the proposed improvement for .NET while leaving other languages unchanged.
Initially, it required to changing the namespace setting in each file accordingly. E.g.:
to
Unfortunately, that is not enough. The thing is that some proto compilers use the file name as a container name when grouping generated code. If the same file name exists in different namespaces, it is not a problem. However, the same file name cannot exist in the same namespace/package more then once, even if it is placed in different directories. Yet, call almost everything
message.proto
. I addressed that with a structural renaming. E.g.:temporal/api/common/v1/message.proto
-->temporal/api/common/v1/common_message.proto
temporal/api/failure/v1/message.proto
-->temporal/api/failure/v1/failure_message.proto
temporal/api/taskqueue/v1/message.proto
-->temporal/api/taskqueue/v1/taskqueue_message.proto
. . .
This did the trick and the protos compiled just fine. Only the .NET generated files should be different from before. Here is a draft PR: #170.
Alternative option:
Note that once we are already forced to do a file renaming like above, we are missing an opportunity to remove some redundant characters in the file names. For example:
temporal/api/common/v1/message.proto
-->temporal/api/v1/common_message.proto
temporal/api/failure/v1/message.proto
-->temporal/api/v1/failure_message.proto
temporal/api/taskqueue/v1/message.proto
-->temporal/api/v1/taskqueue_message.proto
. . .
As a result of that, all the files end up in the same directory. Whether or not it is a good thing is a matter of taste, and various constraints, some of which are discussed below.
Either way, here is a draft PR: #171.
Summary:
I validated that both of the above options compile fine to .NET, using the proposed namespace structure. Other languages should be unchanged. However, the file renaming offers other (new) languages the option to do their own namespace mappings if they wish to.
If people are interested in this proposal, I'd be more than happy to validate all other relevant languages to remain unaffected by this restructuring of source file locations.
Please, let me know what you think.
The text was updated successfully, but these errors were encountered: