Как обрабатывать бизнес-правила при использовании шаблона внедрения зависимостей и репозитория в контроллере?

Таким образом, 99% всех примеров внедрения зависимостей, использующих шаблон репозитория с MVC (или веб-API), показывают что-то похожее на это ниже в действии контроллера (код внедрения конструктора для _repository не указан). Проблема в том, что предполагается, что ничего не нужно обрабатывать в первую очередь (например, правила), и сохранение происходит немедленно:

public ActionResult Create(Person person)
{
    if (ModelState.IsValid)
    {
      _repository.Add(person);
    }

    return View(person);
}

Проблема в том, что если мне нужно обработать правила на уровне домена перед сохранением объекта person? Очевидно, я не использую эту логику в контроллере, а скорее нуждаюсь в ней на уровне предметной области. Какой вариант ниже лучше:

Вариант 1: переназначить все данные сохранения в репозитории для вызова на уровне домена/бизнеса и вывести их из контроллера. Обновленный код будет выглядеть так:

if (ModelState.IsValid)
{
  using (busniessLogic = new MyApp.BusniessLogic(_repository))
  {
       busniessLogic.ProcessRulesAndSavePerson(person);
  }
}

Уровень предметной области может иметь такой метод:

public void ProcessRulesAndSavePerson(Person person)
{
   //process some rules...

   if(rules = true)
   {
     //use injected repository to add now that rules have passed
     _repository.Add(person)
   }
}

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

if (ModelState.IsValid)
{
  using (busniessLogic = new MyApp.BusniessLogic())
  {           
       busniessLogic.ProcessRulesAboutPerson(person);
       _repository.Add(person)
  }
}

Я открыт и для других идей, но склоняюсь к Варианту №1. Мне нравится держать контроллер тонким и просто передавать введенный _repository на уровень, требующий этого.

Любая помощь в этом приветствуется, спасибо!


person atconway    schedule 14.01.2013    source источник


Ответы (2)


Оба варианта создают зависимость от процессора бизнес-логики, вы всегда должны быть осторожны при использовании ключевого слова new, потому что, когда вы пытаетесь выполнить модульное тестирование своего метода, вы почти всегда будете сталкиваться с проблемами, и это противоречит цели внедрения зависимостей.

Вам лучше создать службу, которая обрабатывает сложную бизнес-логику, и внедрить ее в свой контроллер. Если большая часть вашей логики сложна, я бы просто пропустил передачу репозитория контроллеру целиком и просто зависел от службы, которая справится со всем за меня.

Контроллер:

public MyController(IPersonService personService) 
{
    _personService = personService;
}

public ActionResult Create(Person person)
{
    if (ModelState.IsValid)
       _personService.Create(person);
    else
       ......
}

ПерсоналСервис:

public PersonService(IPersonRepository repo) 
{
    _repo = repo;
}

public int Create(Person person)
{
    //businesslogic
    return _repo.Add(person);
}
person Bassam Mehanni    schedule 14.01.2013
comment
Слой обслуживания может быть просто .dll правильно? Означает ли это, что все мои сервисные/бизнес-методы должны определяться интерфейсом? Я думаю, что ответ «да», потому что иначе я не могу правильно внедрить экземпляр в контроллер? - person atconway; 14.01.2013
comment
@atconway точно! и вам все равно нужен интерфейс, таким образом вы можете легко протестировать свой контроллер, внедрив макет в конструктор контроллера (это основная причина, по которой мы используем DI в первую очередь) - person Bassam Mehanni; 14.01.2013
comment
Мне нравится использовать сервисный уровень; избегали до сих пор, но достаточно просты и читались много раз. Итак, вот мои слои: пользовательский интерфейс (MVC), сервисный уровень, уровень моделей/сущностей, репозиторий. Итак, если я использую эту настройку, а бизнес-логика находится на моем сервисном уровне, то мой уровень моделей на самом деле является просто DTO. В этом случае я использую анемическую модель предметной области? Должен ли я перенести бизнес-логику со слоя службы на уровень моделей? - person atconway; 14.01.2013
comment
На эту тему много споров, лично я предпочитаю интеллектуальные модели до определенного момента (если моя модель взаимодействует с другими моделями, я обычно использую сервис. Нет правильного пути, это то, что имеет смысл). вы.Но вы правы, вы можете использовать либо сервисы, либо более умные модели доменов! - person Bassam Mehanni; 14.01.2013
comment
Вы бы сказали, что нормально иметь оба уровень бережливых моделей, и уровень услуг с бизнес-логикой? Я использую EF со сгенерированными классами POCO, и меня не беспокоит, если уровень моделей в основном состоит из DTO. Я предполагаю, что если вместо этого я вырежу сервисный уровень, он станет чем-то вроде прокси, просто передающего вызовы без какой-либо бизнес-логики, верно? - person atconway; 14.01.2013
comment
Это определенно нормально, вы просто должны быть последовательны. Вы должны придумать свод правил и придерживаться их. Вам не нужно определять эти правила заранее, но с опытом вы придете к своим собственным лучшим практикам. Просто не забывайте быть последовательным. - person Bassam Mehanni; 14.01.2013
comment
Это было ясно как звоночек для меня. Я буду использовать сервисный уровень, как я видел раньше, чтобы быть проводником для репозитория и доменного уровня, сохраняя его тонким. Моя модель домена останется надежной как с данными, так и с поведением, поскольку мне на самом деле удобнее всего: martinfowler.com/ bliki/AnemicDomainModel.html Спасибо за помощь! - person atconway; 15.01.2013

Не обязательно лучший способ, но я обычно делаю так:

  • IService (бизнес-логика в вашем случае) внедряется в контроллер (а не в IRepository)
  • Если ModelState действителен => Контроллер будет вызывать методы в IService, которые принимают модель в качестве параметра(ов)
  • IRepository внедряется в экземпляр службы, если это необходимо.

Это позволяет мне: - Иметь возможность модульного тестирования контроллеров, предоставляющих макеты IService. - Держите контроллер в неведении относительно уровня репозитория и доступа к данным.

person qbantek    schedule 14.01.2013