Лучшие практики для 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, где каждый класс репозитория представляет таблицу в базовом хранилище данных.
Я ищу совет по наилучшей практике в ситуации, как описано.
2 ответа
Вы создали сервисный слой, чтобы он действовал как фасад для базовых репозиториев. Этот подход является хорошей практикой для предоставления фасаду клиента с грубым API. Клиентам не нужно беспокоиться о базовых репозиториях.
Сами службы имеют сложный конструктор из-за того, как выполняется DI. Другой подход состоит в том, чтобы использовать абстрактный шаблон фабрики на уровне Service и делать установку сеттера. Эта сложность обновления хранилищ перемещается в отдельный класс, собственную фабрику. Например:
Вы можете установить репозитории вашего тестового сервиса следующим образом вместо конструктора
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;
}
Ниже приведен пример интерфейса для фабрики
public interface IRepoFactory
{
ITransactionRepository TransRepo {get;}
IAccountRepository AccountRepo {get;}
ISystemsRepository SysRepo {get;}
IScheduleRepository SchRepo {get;}
IProfileRepository ProfileRepo {get;}
}
Ниже приведен пример конкретной фабрики, которая обновит все хранилища.
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();}
}
Ниже приведен метод фабрики, который возвращает конкретную фабрику (в вашем случае это будет фабрика EF)
public class RepoFactories
{
public static IRepoFactory GetFactory(string typeOfFactory)
{
return (IRepoFactory)Activator.CreateInstance(Type.GetTypetypeOfFactory)
}
}
Абстрактная фабрика со статическими методами для создания новых и возврата объектов репозитория
// Пример: factoryName = MyProject.Data.EF Factory (это можно добавить в ваш 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;}
}
}
Вот несколько шагов для упрощения (и уменьшения) зависимостей:
Разделите ваш сервис на отдельные сервисы и внедрите их в свой контроллер. Это уменьшит количество зависимостей сервисов. Недостатком является то, что вам нужно будет вводить больше зависимостей в ваши контроллеры. Следующим шагом является разделение контроллеров, когда они становятся сложными. Помните о принципе единой ответственности.
Взгляните на шаблон ограниченного контекста: вы можете попытаться сгруппировать сущности, которые часто объединяются в одном контексте, и внедрить этот контекст в сервис вместо внедрения десятков репозиториев:
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 } }