C # Design Pattern - The Best Design Way for Many Data Sources

Currently, I have an ASP.Net MVC 5 application that uses 3 external data sources (calls are sent to external APIs, responses are deserialized and mapped to business POCOs).

The application currently uses SimpleInjector to enter specific repositories for each data source into the business logic layer for consumption.

The problem is that when you add more data sources (potentially 20-30), the constructor will be huge, and all injections from these repositories seem cumbersome.

Is there a better template / approach to consuming all data sources, rather than using different repositories?

Would a facade or some other template be more suitable?

Very common examples:

public class MyObject(){ public IEnumerable<Cat> Cats { get; set; } public IEnumerable<Dog> Dogs { get; set; } public IEnumerable<Fish> Fish { get; set; } } public class BusinessLogic{ private readonly ISourceARepository _sourceA; private readonly ISourceBRepository _sourceB; private readonly ISourceCRepository _sourceC; public BusinessLogic(ISourceARepository sourceA, ISourceBRepository sourceB, ISourceCRepository sourceC){ _sourceA = sourceA; _sourceB = sourceB; _sourceC = sourceC; } private Dog MapSourceARecordToDog(SourceARecord record){ var result = new Dog(); if(record != null){ result.Name = record.NameField; result.Age = record.Age; } return result; } private Cat MapSourceBRecordToCat(SourceBRecord record){ var result = new Cat(); if(record != null){ result.Name = record.NameField; result.Weight = record.WeightField; } return result; } private Fish MapSourceCRecordToFish(SourceCRecord record){ var result = new Fish(); if(record != null){ result.ID = record.IDField; result.Name = record.NameField; } return result; } public MyObject GetResults(){ var result = new MyObject(); result.Dogs = _sourceA.GetAll().Select(MapSourceARecordToDog).ToList(); result.Cats = _sourceB.GetAll().Select(MapSourceBRecordToCat).ToList(); result.Fish = _sourceC.GetAll().Select(MapSourceCRecordToFish).ToList(); return result; } } public class SourceARespository : ISourceARepository{ public IEnumerable<SourceAResult> GetAll(){ return new List<SourceAResult>(); } } public class SourceBRespository : ISourceBRepository{ public IEnumerable<SourceBResult> GetAll(){ return new List<SourceBResult>(); } } public class SourceCRespository : ISourceCRepository{ public IEnumerable<SourceCResult> GetAll(){ return new List<SourceCResult>(); } } 

Update: This is not a duplicate of the constructor insanity question, because in this scenario the class needs many different data sources, but still bears one responsibility. Therefore, this requires its own explanation and answer.

+4
source share
1 answer

You should enter only one repository for each object in the consumer, which depends on it. You can also adapt the repository to a business class broker.

UPDATE

Based on the information presented in the question and problem statement, here is one of the possible solutions. Define your core infrastructure as follows:

 public abstract class Entity<TEntity, TDomainObject, TIRepository> where TEntity : Entity<TEntity, TDomainObject, TIRepository> where TDomainObject : Entity<TEntity, TDomainObject, TIRepository>.BaseDomainObject, new() where TIRepository : Entity<TEntity, TDomainObject, TIRepository>.IBaseRepository { public class BaseDomainObject {} public interface IBaseRepository { IEnumerable<TDomainObject> GetAll(); IEnumerable<T> GetAllMapped<T>(Func<TDomainObject, T> mapper); } public class BaseRepository : IBaseRepository { public IEnumerable<TDomainObject> GetAll() { return new List<TDomainObject>(); } public IEnumerable<T> GetAllMapped<T>(Func<TDomainObject, T> mapper) { return this.GetAll().Select(mapper); } } } 

Define your source objects as follows:

 public class SourceA : Entity<SourceA, SourceA.DomainObject, SourceA.IRepository> { public class DomainObject : BaseDomainObject { public string Name; public int Age; } public interface IRepository : IBaseRepository {} public class Repository : BaseRepository, IRepository {} } public class SourceB : Entity<SourceB, SourceB.DomainObject, SourceB.IRepository> { public class DomainObject : BaseDomainObject { public string Name; public decimal Weight; } public interface IRepository : IBaseRepository {} public class Repository : BaseRepository, IRepository {} } public class SourceC : Entity<SourceC, SourceC.DomainObject, SourceC.IRepository> { public class DomainObject : BaseDomainObject { public Guid Id; public string Name; } public interface IRepository : IBaseRepository {} public class Repository : BaseRepository, IRepository {} } 

Then define an ISourceRepositoryContext interface similar to this, and add here each source repository interface:

 public interface ISourceRepositoryContext { SourceA.IRepository SourceARepository { get; } SourceB.IRepository SourceBRepository { get; } SourceC.IRepository SourceCRepository { get; } } 

Then define the default implementation for the interface:

 public class DefaultSourceRepositoryContext : ISourceRepositoryContext { public SourceA.IRepository SourceARepository => new SourceA.Repository(); public SourceB.IRepository SourceBRepository => new SourceB.Repository(); public SourceC.IRepository SourceCRepository => new SourceC.Repository(); } 

Define result transport objects:

 public class Dog { public string Name; public int Age; } public class Cat { public string Name; public decimal Weight; } public class Fish { public Guid Id; public string Name; } public class MyObject { public IEnumerable<Cat> Cats { get; set; } public IEnumerable<Dog> Dogs { get; set; } public IEnumerable<Fish> Fish { get; set; } } 

Then use the ISourceRepositoryContext class in your BusinessLogic class:

 public class BusinessLogic { protected ISourceRepositoryContext repositories; public BusinessLogic(ISourceRepositoryContext repositories) { this.repositories = repositories; } public MyObject GetResults(string param1) { return new MyObject() { Dogs = this.repositories.SourceARepository.GetAllMapped (domainObject=>new Dog { Age = domainObject.Age, Name = domainObject.Name }), Cats = this.repositories.SourceBRepository.GetAllMapped (domainObject=>new Cat { Name = domainObject.Name, Weight = domainObject.Weight }), Fish = this.repositories.SourceCRepository.GetAllMapped (domainObject=>new Fish { Id = domainObject.Id, Name = domainObject.Name }), }; } } 

I confirmed that the above compiles under C # 6.0.

I would recommend changing the IRepository for IBusiness to Entity and sharing the data access problems with the IDataAccess interface that only IBusiness developers get through their designers. Then change the ISourceRepositoryContext to ISourceEntities and change the IRepository properties in this interface to IBusiness properties.

The BusinessLogic class is the part that really bothers me. Are you sure that in this class there will not be too many problems? Is it supposed to be a UoW class?

For a more complete solution based on similar methods, check out my answer to this other question: .NET Layer Relationship Management

+1
source

All Articles