1

I have been trying to implement versioning in my ASP.NET Core 6.0 Web API using Microsoft.AspNetCore.Mvc.Versioning.

I want to use separate v1 and v2 folders for my versions:

Controllers
 - v1
   - MyController
 - v2
   - MyController

However, going down this path I end up with different V1.0 and V2.0 folders for everything.

I end up with two separate namespaces and module names as well.

namespace MyAPI.Models.V1 
{
     public class MyModelsV1 
     {

namespace MyAPI.Models.V2 
{
     public class MyModelsV2  
     {

A problem arises in Program.cs with dependency injection. I'll use my identity set up as an example.

builder.Services.AddDefaultIdentity<AppUser>()
    .AddRoles<IdentityRole>()
    .AddClaimsPrincipalFactory<AppUserClaimsPrincipalFactory>()
    .AddDefaultTokenProviders()
    .AddEntityFrameworkStores<AppDbContext>();

Now there is AppUserV1, AppUserV2 and AppUserClaimsPrincipalFactoryV1 and AppUserClaimsPrincipalFactoryV2.

And there will be AppDbContextV1 and AppDbContextV2 as well.

So am I using a wrong approach? Do I just build two separate services for V1 and V2?

builder.Services.AddDefaultIdentity<AppUserV1>()
 ...
builder.Services.AddDefaultIdentity<AppUserV2>()
 ...

How would I implement the v1.0 and v2.0 folder approach in program.cs?

Are there any good end to end guides for this versioning approach? Or should I be doing something else?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Scottish Smile
  • 455
  • 7
  • 22
  • What's the difference between AppUserV1 &AppUserV2? If thet don't have much difference,just considerding inherit V2 from V1? – Ruikai Feng Apr 13 '23 at 08:54
  • Yesh I was thinking that - extend the class - it would mostly work. Except when it comes to things like changing the Id from an int to a "string guid" if I had to do that. – Scottish Smile Apr 13 '23 at 11:27
  • FYI, `Microsoft.AspNetCore.Mvc.Versioning` is EOL since .NET 6.0 and has been supplanted by [Asp.Versioning.Mvc](https://www.nuget.org/packages/Asp.Versioning.Mvc). – Chris Martinez Apr 13 '23 at 14:51
  • Versioning your models is fine and make sense. Versioning your application users, not so much. If possible, I would have only one implementation of application users. Unless some part of them is exposed over the wire or changes the behavior of APIs, they should otherwise be free to make changes. – Chris Martinez Apr 13 '23 at 14:53
  • Thanks for the info Chris. What about versioning Services? Like SendEmailService, I assume that would also be ok. Other things like Factory Setups maybe not (AppUserClaimsPrincipalFactory or LoggerFactory) may be in the same boat as Appuser. – Scottish Smile Apr 13 '23 at 23:34

1 Answers1

2

I want to use separate v1 and v2 folders for my versions:

I would argue that there are two main possible scenarios:

  1. You have breaking changes between the APIs but internal implementation is +/- the same. In this case usually you will want both versions of API to operate over the same entities and the Web layer should perform version specific logic over them (i.e. like adapting old versions to new ones, etc.). In this case I would argue that you should better work with some kind of tiered architecture and have versioning applied only to Web and possibly business layer models/DTOs (depending on concrete case). Cross-cutting concerns, DAL and so on should not be affected.

  2. There is a complete overhaul/rewrite is happening between. I would argue that this is far more rare case and in this case your approach is fine, but I would go even a step further and possibly introduce new version of service as separate project and handle the routing on some kind of proxy.

Now there is AppUserV1, AppUserV2 and AppUserClaimsPrincipalFactoryV1 and AppUserClaimsPrincipalFactoryV2.

I would argue that this is an example of cross-cutting concern which should be shared between API versions. In some rare occasions of changes like "from an int to a string guid" I would argue that it would be better to implement two different auth flows which will use different fields as Id (i.e. you add new guid field and set it as unique index filling for old records, potentially changing PK to be the new field, though having guid as PK is considered to be questionable approach by some DBAs)

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    _Any_ change is a breaking change. Backward compatibility is a fallacy. A server cannot guarantee that a change, even additive, doesn't break a client. That said, I agree that application users and the security model is a cross-cutting concern and should be shared between versions to the greatest extent possible. They do not need to be versioned unless some change in data or behavior over the wire occurs. – Chris Martinez Apr 13 '23 at 14:56
  • @ChrisMartinez yes, I agree, change management should be added to the list of caching and naming =) – Guru Stron Apr 13 '23 at 15:09
  • Ah, so versioning has limits. For AppUser and DbContext there will just be things that will exist for the lifetime of the app unless I set up a separate new project? Thanks for the info, there's not much talk of this nity-gritty online so it is valuable. – Scottish Smile Apr 13 '23 at 23:31