@SamaraSoucy-MSFT is correct - all of the options are valid. The best way is highly subjective, but I will elaborate further on the specific options for API Versioning.
API Versioning is orthogonal to your infrastructure. If you want to split things up into separate applications or add other levels of isolation, that is a supported scenario. The notion of Version Advertisement describes this in some detail. In short, it is possible for your application to advertise API versions that are not hosted in that particular implementation. There are many ways that version advertisement can be achieved, so there is no specific guidance on how to do that. In the simplest model, you'd simply update your application with new conventions, attributes, etc. However, there is very likely a desire to make no change whatsoever to existing deployments. This means that you need to come up with a way to update this information outside the app (ex: config, db, etc) or forego version advertisement. This partially depends on how isolated each implementation. If you don't care about advertising available or deprecated API versions, then this is a non-issue.
There are at least 4 ways you can achieve your goals using API Versioning.
Option 1 - Copy, Paste, Replace
The Copy, Paste, Replace (CPR) method involves literally copying and pasting the previous version of a service into a new folder and new code namespace. This forms the baseline of a new version from the previous version without changing the original. The differences in the new version are then replaced as necessary. This process can repeat for as long as you want.
If your changes are fairly minimal between versions, this can be a very simple and effective strategy. A key drawback is bloat and implementation coupling over time. If you have a solid versioning policy (e.g. N-2), then that can minimize the impact. The biggest challenge would making significant implementation changes, which do not make sense to co-host or could result in changing previous versions. Such an example would be trying to host 1.0
with ASP.NET Web API and 2.0
in ASP.NET Core in the same implementation.
I have seen this approach used a few times and it can be effective.
Option 2 - Inject and Compose
In this variant, you would implement each version in a separate assembly of APIs. You would have an API host application which injects and composes all APIs from each versioned assembly. This provides complete isolation between implementations, which still having all the benefits of API Versioning.
Like Option 1, you still have to consider bloat; especially since each assembly likely has different dependencies. It would also be quite difficult to host APIs authored for different platforms, but it should be technically feasible.
Option 3 - Host Header Mapping
Most web servers have some way of mapping the Host
header to different applications. You can use this technique to host each version of the application as an independent application.
Option 4 - Gateway
Using a gateway provides the most flexibility, but can be the most challenging to setup. This method can use any number of routing methods such as DNS, URL rewriting, or URL redirection based on available version information in a request. The endpoint is then likely an isolated application such as in Option 3. The gateway is responsible for forwarding or redirecting a request to the correct version endpoint.
I use the term gateway loosely. This could an existing service or platform, hardware-based, or of your own custom design. It will depend on your needs and whether an out-of-the-box solution can satisfy them.
Additional Considerations
These certainly aren't the only options nor are they mutually exclusive. You could combine any variation of them to suit your needs.
Choosing Option 3 or Option 4 will come with some challenges for API Versioning since the physical isolation barriers transcend each deployed application. You'll either need to come up with a way to update advertised versions or discard the notion entirely - as mentioned above. If you end up not advertising versions at all, then each deployed implementation should be a single, independent codebase that likely negates the need for API Versioning at all since it's handled at a abstraction layer.
Your overall versioning strategy and policy will also play a factor. If you use symmetrical verisioning across your services, the URL design and API versions are consistent across the board, but come with added deployments to satisfy this requirement. If you choose to have independent, evolving services, then version discovery and advertisement becomes more interesting. For example, your policy might state that a deprecated version will be sunset after 6 months. Clients should then detect this advertisement by away of instrumenting the api-deprecated-versions
response header. Version discovery can be supported by way of the HEAD
and/or OPTIONS
method.