I have a question that may be quite trivial for some. I’m making an application in .NET where I’m trying to have split layers. Application (abstraction only) and Infrstructure (implementation). My problem is that I want to use Azure TableClient to store data from an Azure storage table. But the method to store requires an implementation of the ITableEntity interface on that entity. I just can’t declare this in the application layer for the storage method because I would make a dependency on Azure.Data.Tables and I don’t want that. I don’t really know how to write my interface with this and then "overlay" it I guess.
Application:
public interface ITableStorage
{
public Task SaveItemsAsync<T>(IEnumerable<T> entites, string tableName = nameof(T)) where T : ITableEntity; // where T : ITableEntity is problem
}
Infrastructure:
public async Task SaveItemsAsync<T>(IEnumerable<T> entites, string tableName = nameof(T)) where T : ITableEntity
{
var tableClient = _tableServiceClient.GetTableClient(tableName);
......
}
Thanks in advance for the advice.
2
Answers
Base your interface purely on what you want it to achieve without thinking of the implementation. E.g.
Once you’ve done that add the implementation.
As you see there’s no implementation details at all in your interface. Since you only work with
Customer
you’re free to map it to any class that you may require for your database within your implementation of the service.On solution is to use multiple representations of your "entity". The application uses the canonical representation that includes methods, logic etc. This is then converted to a type suitable for storage in Azure, or something else that stores entities.
This does require multiple representations of the same data, but it does add some flexibility. If you change the data model you can keep the legacy Azure representation so you can read old data, while using a new entity type when saving. This is similar to the concept of Data Transfer Object.
To do the conversion between types there are a few options:
You likely want some common interface for entities on the application side, so the compiler can help to ensure you are only saving objects that are designed to be saved.
A very simple example to illustrate the idea: