I am designing an API using .NET core 6 C# which has 2 versions. For v1 I have something like the following for the business logic interface (fictional names and simple fields just to facilitate understanding):
public interface IV1Business
{
Task<MyModel> Create(string configuration, string metadata);
Task<MyModel> GetOne(string id);
Task<IEnumerable<MyModel>> GetAll(IEnumerable<string> ids);
Task Update(string id, string, configuration, string metadata);
Task Delete(string id);
}
Then I have 3 implementations A, B and C.
But not all of them follow the same contract, for example, C is allowed to operate with Get, B can do Get and Update, and A has everything, which produces the following:
public class C : IV1Business
{
public async Task<MyModel> Create(string configuration, string metadata)
{
throw new NotImplementedException();
}
public async Task<MyModel> GetOne(string id)
{
// implementation
}
public async Task<IEnumerable<MyModel>> GetAll(IEnumerable<string> ids)
{
// implementation
}
public async Task Update(string id, string, configuration, string metadata)
{
throw new NotImplementedException();
}
public async Task Delete(string id)
{
throw new NotImplementedException();
}
}
This is not good, it does not make sense to have a contract where there are holes like that.
So, I have applied the interface segregation principle (ISP) and extracted some interfaces so starting by minimum operations (C) until the use case of A which does everything.
public interface IV1GetAllBusiness
{
Task<MyModel> GetOne(string id);
Task<IEnumerable<MyModel>> GetAll(IEnumerable<string> ids);
}
public interface IV1UpdateBusiness
{
Task Update(string id, string, configuration, string metadata);
}
public interface IV1GetAndUpdateBusiness : IV1GetAllBusiness, IV1UpdateBusiness
{
}
public interface IV1Business : IV1GetAndUpdateBusiness
{
Task<MyModel> Create(string configuration, string metadata);
Task Delete(string id);
}
For v2, the contract has changed, and now there are different routes to update the configuration and metadata of MyModel, which makes me believe v1 and v2 are incompatible and they need different interfaces.
So my question is, does it make sense to have this approach for v1 using some sort of role bases interfaces and avoid the throws for not-implemented methods?
Is there any other approach that would fit well on this situation for multiple versions of the API?