MVC DDD: можно ли использовать репозитории вместе со службами в контроллере?

большую часть времени в сервисном коде у меня было бы что-то вроде этого:

public SomeService : ISomeService
{
    ISomeRepository someRepository;
    public Do(int id)
    {
        someRepository.Do(id);
    }
}

так что это своего рода избыточно

поэтому я начал использовать репозитории непосредственно в контроллере

это нормально ? есть ли какая-то архитектура, которая делает это ?

5 ответов


вы потеряли способность иметь бизнес-логику между ними.

Я не согласен с этим.

если бизнес-логика там, где она должна быть - в модели домена, то вызов repo в контроллере (или лучше использовать связыватель модели для этого), чтобы получить aggregate root и вызвать метод на нем, кажется мне совершенно прекрасным.

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


Я видел, как несколько человек упоминали об использовании модельных связующих для вызова в РЕПО в последнее время. Откуда взялась эта безумная идея?

Я считаю, что мы говорим о 2 разных вещах. Я подозреваю, что ваш "модельный связующий" означает использование модели одновременно в качестве модели представления и привязку измененных значений из пользовательского интерфейса непосредственно к нему (что само по себе неплохо, и в некоторых случаях я бы пошел по этому пути).

мой 'model binder' - это класс, который реализует'IModelBinder', который принимает репозиторий в конструкторе (который вводится и, следовательно, может быть расширен, если нам нужно кэширование с некоторой базовой композицией) и использует его перед вызовом действия для извлечения aggregate root и замены int id или Guid id или string slug или whatever аргумент действия с объектом недвижимого достояния. Объединение этого с аргументом модели представления ввода позволяет нам писать меньше кода. Что-то вроде этого:--7-->

public ActionResult ChangeCustomerAddress
 (Customer c, ChangeCustomerAddressInput inp){
  c.ChangeCustomerAddress(inp.NewAddress);
  return RedirectToAction("Details", new{inp.Id});
}

In мой фактический код это немного сложнее, потому что он включает проверку ModelState и некоторую обработку исключений, которые могут быть брошены изнутри модели домена (извлечены в метод расширения контроллера для повторного использования). Но не более того. До сих пор самое длинное действие контроллера составляет ~10 строк.

вы можете увидеть рабочую реализацию (довольно сложную и (для меня) ненужную сложную) здесь.

вы просто делаете приложения CRUD с Linq to Sql или пробовать что-то с реальной логикой домена?

как вы можете (надеюсь) видеть, такой подход на самом деле почти заставляет нас двигаться к задача приложение вместо CRUD на основе одного.

выполняя доступ ко всем данным на уровне сервиса и используя IOC, вы можете получить множество преимуществ AOP, таких как невидимое кэширование, управление транзакциями и простой состав компонентов, которые я не могу себе представить, вы получаете с моделью связующее вещество.

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

пожалуйста, просветите меня.

Я не уверен, что я это сделал. Я не думаю, что я сам просветленный. :)


здесь - мой текущий базовый класс связующего модели. здесь одно из действий контроллера из моего текущего проекта. И здесь "отсутствие" бизнес-логики.


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

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

Я бы всегда пошел этот маршрут:Repositories -> Services -> UI. Как только вы не думаете, что вам нужен бизнес-уровень, требования меняются, и вам придется все переписать.


вот в чем дело.

"бизнес-логика" должна находиться в сущности и объекты-значения.

репозитории имеют дело только с AggregateRoots. Итак, используя ваши репозитории напрямую в контроллеры чувствую, что вы лечите, что действия, как ваши "услуги". Кроме того, поскольку ваш AggregateRoot может ссылаться только на другие ARs по своему идентификатору, вам может потребоваться вызвать более одного РЕПО. Это действительно становится неприятным очень быстро.

Если вы собираетесь иметь службы, убедитесь, что вы выставляете POCOs, а не фактический AggregateRoot и его члены.

ваше РЕПО не должно выполнять никаких операций, кроме создания, извлечения, обновления и удаления материалов. Возможно, у вас есть некоторые индивидуальные извлечения на основе определенных условий, но это все. Поэтому, имея метод в вашем РЕПО, который соответствует одному в вашем сервисе... кодовый запах прямо здесь.

ваш сервис ориентирован на API. Думать об этом... если ты соберешь это ... сервис в .dll для меня, как бы вы создали свои методы таким образом, чтобы мне было легко узнать, что может сделать ваш сервис? Услуга.Обновление (объект) не имеет большого смысла.

и я даже не говорил о CQRS... где все становится еще интереснее.

ваш веб-Api-это просто клиент вашего сервиса. Ваша служба может быть использована другой службой, верно? Так что подумай об этом. Скорее всего, вам понадобится служба для инкапсуляции операций AggregateRoots, обычно, создавая их или извлекая их из РЕПО, сделайте что-то с этим, а затем верните результат. Обычно.

смысл?


моей грубая практика для DDD / MVC:

  • контроллеры специфичны для приложений, поэтому они должны содержать только специфические для приложений методы и методы вызова служб.
  • все методы государственной службы обычно являются атомарными транзакциями или запросами
  • только Службы инстанцируют и вызывают репозитории
  • мой домен определяет IContextFactory и IContext (массивная протекающая абстракция как члены IContext IDBSet)
  • каждое приложение имеет какой-то Состав Root, который в основном создает экземпляр фабрики контекста для передачи служб (вы можете использовать контейнер DI, но не большое дело)

Это заставляет меня держать мой код, и доступ к данным из моих контроллеров. Я нахожу это хорошей дисциплиной, учитывая, насколько я свободен, когда я не следую вышесказанному!


даже с "богатой моделью домена" вам все равно понадобится доменная служба для обработки бизнес-логики, которая включает в себя несколько объектов. Я также никогда не видел CRUD без некоторой бизнес-логики, но в простом примере кода. Я всегда хотел бы пойти по пути Мартина, чтобы мой код был простым.