skip to Main Content

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


  1. Base your interface purely on what you want it to achieve without thinking of the implementation. E.g.

    public interface ICustomerService
    {
        public Task AddNewCustomerAsync<T>(Customer customer);
    }
    

    Once you’ve done that add the implementation.

    public class TableStorageCustomerService : ICustomerService
    {
        public async<Task> AddNewCustomerAsync<T>(Customer customer)
        {
            //map your customer class to your table entity
            //save the table entity to your table storage
        }
    }
    

    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.

    Login or Signup to reply.
  2. 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:

    1. use a switch over the type
    2. Use the visitor pattern. This has the advantage that you can be forced to create an azure-type for every application-type. So less chance of forgetting.

    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:

    public interface IMyEntity{}
    public class MyEntity {};
    public interface IEntityStorage{
        void Save(IMyEntity entity);
    }
    
    public class MyEntityAzure : ITableEntity {
        public MyEntityAzure(MyEntity entity){
            // copy properties etc.
        }
        public MyEntity ToModel(){
           // Convert back to application entity
        }
    }
    public class AzureStorage : IEntityStorage{
        private AzureStorage storage;
        public void Save(IMyEntity entity){
            switch(entity){
                case MyEntity  myEntity:
                    storage.Save(new MyEntityAzure(myEntity));
                    break;
                // All other entity classes
             }
         }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search