most of the time in the service code I would have something like this:
public SomeService : ISomeService
{
ISomeRepository someRepository;
public Do(int id)
{
someRepository.Do(id);
}
}
so it's kinda redundant
so I started to use the repositories directly in the controller
is this ok ? is there some architecture that is doing like this ?
Вы теряете способность иметь промежуточную бизнес-логику.
Я не согласен с этим.
Если бизнес-логика находится там, где она должна быть — в модели домена, то вызов репозитория в контроллере (или лучше — использовать для этого связыватель модели) для получения совокупного корня и вызова метода для него кажется мне совершенно нормальным.
Службы приложений следует использовать, когда требуется слишком много технических деталей, которые могут запутать контроллеры.
В последнее время я видел, как несколько человек упоминали об использовании связывателей моделей для обращения к репозиторию. Откуда взялась эта сумасшедшая идея?
Я полагаю, что мы говорим здесь о двух разных вещах. Я подозреваю, что ваше «связывание модели» означает одновременное использование модели в качестве модели представления и привязку измененных значений из пользовательского интерфейса непосредственно к ней (что само по себе неплохо, и в некоторых случаях я бы пошел по этому пути).
Мое «связывание модели» — это класс, который реализует « IModelBinder», который берет репозиторий в конструкторе (который внедряется и, следовательно, может быть расширен, если нам нужно кэширование с некоторой базовой композицией) и использует его. перед вызовом действия для извлечения сводного корня и замены int id
или Guid id
или слага строки
или любого
аргумента действия реальным объектом домена . Сочетание этого с аргументом входной модели представления позволяет нам писать меньше кода. Что-то вроде этого:
public ActionResult ChangeCustomerAddress
(Customer c, ChangeCustomerAddressInput inp){
c.ChangeCustomerAddress(inp.NewAddress);
return RedirectToAction("Details", new{inp.Id});
}
В моем реальном коде это немного сложнее, потому что он включает проверку ModelState и некоторую обработку исключений, которые могут быть вызваны внутри модели предметной области (извлекаются в метод расширения контроллера для повторного использования). Но не более того. На данный момент самое длинное действие контроллера составляет ~ 10 строк.
Вы можете увидеть рабочую реализацию (достаточно изощренную и (для меня) ненужную сложность) здесь.
Вы просто делаете CRUD-приложения с помощью Linq To Sql или пробуете что-то с реальной доменной логикой?
Как вы можете (надеюсь) видеть, такой подход на самом деле почти заставляет нас двигаться к на основе задач вместо приложения на основе CRUD.
Осуществляя доступ ко всем данным на уровне службы и используя IOC, вы можете получить множество преимуществ АОП, таких как невидимое кэширование, управление транзакциями и простота компоновки компонентов, которые я не могу себе представить при связывании моделей.
... и наличие нового уровня абстракции, который предлагает смешивать инфраструктуру с логикой предметной области и терять изоляцию модели предметной области.
Пожалуйста, просветите меня.
Не уверен, что знал. Я не думаю, что я сам просветленный. :)
Вот мой текущий базовый класс связывателя модели. Вот одно из действий контроллера из моего текущего проекта. И вот "отсутствие" бизнес-логики.
Если вы используете репозитории в своих контроллерах, вы переходите прямо из уровня данных на уровень представления. Вы теряете способность иметь промежуточную бизнес-логику.
Теперь, если вы говорите, что будете использовать Службы только тогда, когда вам нужна бизнес-логика, и использовать Репозитории везде, где бы то ни было, ваш код станет кошмаром. Уровень представления теперь вызывает как бизнес-уровень, так и уровень данных, и у вас нет четкого разделения задач.
Я бы всегда шел по этому пути: Репозитории -> Сервисы -> Пользовательский интерфейс
. Как только вы считаете, что бизнес-уровень вам не нужен, требования меняются, и вам придется переписывать ВСЕ.