Лучшие практики для 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;}
    }
}

Вот несколько шагов для упрощения (и уменьшения) зависимостей:

  1. Разделите ваш сервис на отдельные сервисы и внедрите их в свой контроллер. Это уменьшит количество зависимостей сервисов. Недостатком является то, что вам нужно будет вводить больше зависимостей в ваши контроллеры. Следующим шагом является разделение контроллеров, когда они становятся сложными. Помните о принципе единой ответственности.

  2. Взгляните на шаблон ограниченного контекста: вы можете попытаться сгруппировать сущности, которые часто объединяются в одном контексте, и внедрить этот контекст в сервис вместо внедрения десятков репозиториев:

    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
        }
    }
    
Другие вопросы по тегам