Рекомендации по 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;}
    }
}