2

My goal is to have previous versions be immutable: they should not change in definition and function. The API is build with ASP.NET Core based on .NET 4.7.2(cause of dependencies) and hosted as Azure App Service.

Preferably, I don't want to clutter my code by adding "version knowledge" to it. Also if the versions can be hosted under the same base URL that would be nice too.

My research:

  1. ASP.NET API Versioning

With this you have full control over versioning inside your application. But when a new version is released, all older versions are also updated and thus can change. This means you cannot mutate any existing function but have to create new versions, like Scott does in URL PATH SEGMENT VERSIONING.

  1. Azure Deploymentslots

As the documentation explains, this should be used for staging. However this could also be used to 'store' your versions, since every deploymentslot is hosted without any relation to each other.

  1. Virtual Applications

Hosting versions as virtual applications is also an option. However all settings of the App Service are shared between these virtual applications. Which means when changing any setting this will impact every version.

  1. Azure Web App for Containers

My knowledge of this is limited, but from what I have read this is also an option. Creating images based on versions of your application, uploading these to an Azure Container Registery. Then create an App Service for each version using these images.

Richard Bos
  • 774
  • 5
  • 18
  • Before providing an answer, I wanted to get clarification about what you mean regarding: **_"...when a new version is released, all older versions are also updated and thus can change. This means you cannot mutate any existing function but have to create new versions..."_** The implementation details of how you achieve isolation can vary widely. There are definitely ways to leave the code of previous versions completely untouched. Older versions are only changed if _you_ change them. – Chris Martinez Jan 23 '20 at 20:13
  • What I mean is that with API versioning, previous versions are included in the API for as long as you leave the code intact. But to achieve immutability, all code used by previous versions should not be changed. So in order to change functionality of a function, you would have to clone the entire chain of functions to make sure old versions do not change. – Richard Bos Jan 24 '20 at 09:40
  • 1
    Gotcha. I'll provide a more detailed answer below. – Chris Martinez Jan 24 '20 at 22:41
  • @ChrisMartinez Thanks, looking forward to your response! – Richard Bos Jan 31 '20 at 15:19

2 Answers2

2

The best way is the one that fits your use case- all of the ways you brought up are valid.

If you want to be able to have separate settings without changing any code to do the versioning than your options are deployment slots and containers. Between the two, slots are going to be easier to use if you aren't familiar with Docker.

As you mentioned, Virtual Apps does something similar, but you can't separate configurations.

Keep in mind that this means running multiple copies of your app on the same App Service, so you will need to scale that appropriately. That's probably the biggest advantage of using API Versioning, even if it does mean some different coding practices. You are still running just one copy. As you get more and more versions, the cost of running multiple copies of the app will go up, unless you start deprecating older ones.

All Azure websites use the https://{app_name}.azurewebsites.net url, so technically they all have the same base. If you are looking for them all to have the same subdomain, then API versioning or Virtual Applications are the way to go unless you want to build in some redirect logic. Of course, with custom domains you have more control over how the DNS records are mapped.

PerfectlyPanda
  • 3,271
  • 1
  • 6
  • 17
  • I would add that deployment slots and containers are not mutually exclusive. Now it is possible to use a container registry as a source for a deployment slot in a web app. – bN_ Sep 13 '21 at 13:23
1

@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.

Chris Martinez
  • 3,185
  • 12
  • 28
  • Thanks for taking the time to answer. Keeping it short: Version Advertisement is not needed, there's no versioning policy (so no Option 1). Option3+4: for my case, an API gateway is already present. Option 2 is not entirely clear to me. What do you mean by Inject and Compose? Does this mean every version is isolated into a DLL(assembly) which is than loaded at runtime? – Richard Bos Feb 18 '20 at 10:55
  • Correct. This would most likely be achieved by way of dependency injection (DI). As long as they are all loaded and discovered by the **IActionDescriptorCollectionProvider** service, then API Versioning will continue to work without any other changes. – Chris Martinez Feb 20 '20 at 17:48