aspnetcore/grpc/versioning.md
New features added to an app can require gRPC services provided to clients to change, sometimes in unexpected and breaking ways. When gRPC services change:
The gRPC protocol is designed to support services that change over time. Generally, additions to gRPC services and methods are non-breaking. Non-breaking changes allow existing clients to continue working without changes. Changing or deleting gRPC services are breaking changes. When gRPC services have breaking changes, clients using that service have to be updated and redeployed.
Making non-breaking changes to a service has a number of benefits:
These changes are non-breaking at a gRPC protocol level and .NET binary level.
The following changes are non-breaking at a gRPC protocol level, but the client needs to be updated if it upgrades to the latest .proto contract or client .NET assembly. Binary compatibility is important if you plan to publish a gRPC library to NuGet.
csharp_namespace will change the namespace of generated .NET types. This isn't a gRPC protocol breaking change, but the client needs to be updated if it upgrades to the latest contract.The following items are protocol and binary breaking changes:
When making non-breaking changes, you must also consider whether older clients can continue working with the new service behavior. For example, adding a new field to a request message:
Behavior compatibility is determined by your app-specific code.
Services should strive to remain backwards compatible with old clients. Eventually changes to your app may require breaking changes. Breaking old clients and forcing them to be updated along with your service isn't a good user experience. A way to maintain backwards compatibility while making breaking changes is to publish multiple versions of a service.
gRPC supports an optional package specifier, which functions much like a .NET namespace. In fact, the package will be used as the .NET namespace for generated .NET types if option csharp_namespace is not set in the .proto file. The package can be used to specify a version number for your service and its messages:
The package name is combined with the service name to identify a service address. A service address allows multiple versions of a service to be hosted side-by-side:
greet.v1.Greetergreet.v2.GreeterImplementations of the versioned service are registered in Startup.cs:
app.UseEndpoints(endpoints =>
{
// Implements greet.v1.Greeter
endpoints.MapGrpcService<GreeterServiceV1>();
// Implements greet.v2.Greeter
endpoints.MapGrpcService<GreeterServiceV2>();
});
Including a version number in the package name gives you the opportunity to publish a v2 version of your service with breaking changes, while continuing to support older clients who call the v1 version. Once clients have updated to use the v2 service, you can choose to remove the old version. When planning to publish multiple versions of a service:
Publishing multiple versions of a service duplicates it. To reduce duplication, consider moving business logic from the service implementations to a centralized location that can be reused by the old and new implementations:
Services and messages generated with different package names are different .NET types. Moving business logic to a centralized location requires mapping messages to common types.