Рекомендации по IoC в сложном сервисном слое
Я разрабатываю приложение MVC, я использую Unity для IoC. Мое приложение в основном состоит из слоя пользовательского интерфейса, уровня обслуживания и уровня хранилища.
Мой типичный контроллер:
public class TestController : Controller
{
private ITestService testServ;
public TestController(ITestService _testServ)
{
testServ= _testServ;
}
public ActionResult Index()
{
testServ.DoSomething();
return View();
}
}
Ничего необычного, каждый из моих контроллеров имеет введенный служебный объект. Таким образом, мои объекты уровня обслуживания выполняют сложные бизнес-правила, объединяющие информацию из разных хранилищ. Используя IoC, я нахожу, что мои конструкторы выглядят слишком сложными, но поскольку служба требует доступа ко многим репозиториям, я не вижу никакого способа обойти это.
Типичный класс в моем сервисном слое будет выглядеть следующим образом:
public class TestService : ITestService
{
private ITransactionRepository transRepo;
private IAccountRepository accountRepo;
private ISystemsRepository sysRepo;
private IScheduleRepository schRepo;
private IProfileRepository profileRepo;
public TestService(ITransactionRepository _transRepo;
IAccountRepository _accountRepo;
ISystemsRepository _sysRepo;
IScheduleRepository _schRepo;
IProfileRepository _profileRepo)
{
transRepo = _transRepo;
accountRepo = _accountRepo;
sysRepo = _sysRepo;
schRepo = _schRepo;
profileRepo = _profileRepo;
}
public DoSomething()
{
//Implement Business Logix
}
}
Для нескольких объектов моего уровня обслуживания требуется 10 или более репозиториев. В моем хранилище используется Entity Framework, где каждый класс репозитория предоставляет таблицу в базовом хранилище данных.
Я ищу советы по лучшей практике в ситуации, описанной ниже.
Ответы
Ответ 1
Ниже приведены некоторые шаги для упрощения (и уменьшения) зависимостей:
-
Разделите службу на отдельные службы и введите их в свой контроллер. Это уменьшит количество зависимостей сервисов. Недостатком является то, что вам нужно будет добавить больше зависимостей к вашим контроллерам. Следующим шагом является разделение контроллеров, когда они становятся сложными. Помните о Принцип единой ответственности.
-
Взгляните на Ограниченный контекст: вы можете попытаться сгруппировать объекты, которые часто объединяются в одном контексте и вставляют этот контекст в службу вместо инъекции десятков репозиториев:
public class TestService : ITestService
{
private readonly ITestData testData; // represents a bounded context
public TestService(ITestData testData)
{
this.testData = testData;
}
public void DoSomething()
{
this.testData.Transactions.Add(...); //It gives you access to Transactions repository
}
}
Ответ 2
Вы создали сервисный уровень, чтобы он действовал как фасад для базовых репозиториев. Этот подход является хорошей практикой для предоставления фасада клиенту с грубым API. Клиентам не нужно беспокоиться о базовых репозиториях.
Сама служба имеет сложный конструктор из-за того, как выполняется DI. Другой подход заключается в использовании абстрактного шаблона factory на уровне сервиса и выполнения инъекции сеттера. Эта сложность новички репозиториев переносится в отдельный класс, а factory.
Например:
Вы можете установить репозитории вашей тестовой службы следующим образом, а не конструктор
public class TestService : ITestService
{
private ITransactionRepository transRepo = DataAccess.transRepo;
private IAccountRepository accountRepo = DataAccess.accountRepo;
private ISystemsRepository sysRepo = DataAccess.sysRepo;
private IScheduleRepository schRepo = DataAccess.schRepo ;
private IProfileRepository profileRepo = DataAccess.profileRepo;
}
Ниже приведен пример интерфейса для factory
public interface IRepoFactory
{
ITransactionRepository TransRepo {get;}
IAccountRepository AccountRepo {get;}
ISystemsRepository SysRepo {get;}
IScheduleRepository SchRepo {get;}
IProfileRepository ProfileRepo {get;}
}
Ниже приведен пример конкретного factory, который будет обновлять все репозитории.
public class EfFactory : IRepoFactory
{
public ITransactionRepositry TransRepo { return new TransactionRepository();}
public IAccountRepository AccountRepo {return new AccountRepository();}
public ISystemsRepository SysRepo {return new SystemRepository();}
public IScheduleRepository SchRepo {return new SchRepository();}
public IProfileRepository ProfileRepo {return new ProfileRepository();}
}
Ниже приведен метод factory, который вернет конкретный factory (в вашем случае это будет EF Factory)
public class RepoFactories
{
public static IRepoFactory GetFactory(string typeOfFactory)
{
return (IRepoFactory)Activator.CreateInstance(Type.GetTypetypeOfFactory)
}
}
Абстрактный factory со статическими методами для создания новых и возврата объектов репозитория
//Пример: factoryName = MyProject.Data.EFFactory(это можно добавить в ваш web.config или app.config)
Public static class DataAccess
{
private static readonly string DbfactoryName= ConfigurationManager.AppSettings.Get("factoryName");
private static readonly IRepoFactory factory = RepoFactories.GetFactory(DbfactoryName);
public static ITransactionRepositry transRepo
{
get {return factory.TransRepo;}
}
public static IAccountRepository accountRepo
{
get {return factory.AccountRepo;}
}
}