Непогана практика контролера безпосередньо викликати сховище. «Служба» - це просто ще один інструмент, тому користуйтеся нею, де це має сенс.
НиколайДанте прокоментував:
... Підберіть правильну схему для правильного застосування. Я б сказав, що ви повинні зробити свою заяву послідовною.
Я не думаю, що послідовність є найважливішим аспектом. Клас "служби" призначений для інкапсуляції певної логіки вищого рівня, тому контролеру не потрібно його реалізовувати. Якщо для даної операції не потрібна "логіка вищого рівня", просто перейдіть безпосередньо до сховища.
Для того, щоб сприяти гарному розділенню проблем та перевіряемості, сховище повинно бути залежністю, яку ви вводите в службу через конструктор:
IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);
service.DoSomething(...);
Якщо для пошуку записів у базі даних потрібен якийсь параметризований запит, клас обслуговування може бути хорошим місцем для прийняття вашої моделі перегляду та побудови запиту, який потім виконується сховищем.
Так само, якщо у вас є складна модель перегляду форми, сервісний клас може інкапсулювати логіку створення, оновлення та видалення записів методами виклику у ваших Доменних моделях / Субстанціях, а потім зберігати їх за допомогою репозиторію.
Якщо піти в зворотному напрямку, якщо вашому контролеру потрібно отримати запис за його Id, то делегування до сервісного об’єкта для цього схоже на удари пальця кувалдою - це набагато більше, ніж потрібно.
Я виявив, що контролер знаходиться в кращому положенні для обробки транзакції або об'єкта Unit Of Work . Потім контролер або об'єкт Unit Of Work делегуватиметься об'єктам служби для складних операцій, або перейде безпосередньо до сховища для простих операцій (наприклад, пошуку запису за Id).
public class ShoppingCartsController : Controller
{
[HttpPost]
public ActionResult Edit(int id, ShoppingCartForm model)
{
// Controller initiates a database session and transaction
using (IStoreContext store = new StoreContext())
{
// Controller goes directly to a repository to find a record by Id
ShoppingCart cart = store.ShoppingCarts.Find(id);
// Controller creates the service, and passes the repository and/or
// the current transaction
ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);
if (cart == null)
return HttpNotFound();
if (ModelState.IsValid)
{
// Controller delegates to a service object to manipulate the
// Domain Model (ShoppingCart)
service.UpdateShoppingCart(model, cart);
// Controller decides to commit changes
store.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
return View(model);
}
}
}
}
Я вважаю, що поєднання служб та робота із сховищами безпосередньо є цілком прийнятним. Ви можете додатково інкапсулювати транзакцію в об'єкт "Unit Of Work", якщо відчуєте потребу.
Розбиття обов'язків відбувається так:
- Контролер контролює потік програми
- Повертає "404 не знайдено", якщо кошик відсутній у базі даних
- Повторно надає форму з повідомленнями про перевірку, якщо перевірка не вдалася
- Зберігає кошик для покупок, якщо все перевіряється
- Контролер делегує до класу обслуговування для виконання бізнес-логіки у ваших Доменних моделях (або об'єктах). Об'єкти обслуговування не повинні реалізовувати ділову логіку! Вони виконують логіку бізнесу.
- Контролери можуть делегувати безпосередньо сховища для простих операцій
- Об'єкти сервісу беруть дані у моделі перегляду та делегують доменним моделям для виконання бізнес-логіки (наприклад, сервіс-об’єкт викликає методи доменних моделей перед викликом методів у сховищі)
- Об'єкти служби делегуються до сховищ для збереження даних
- Контролери повинні:
- Керуйте терміном дії транзакції, або
- Створіть об’єкт «Підрозділ роботи», щоб керувати тривалістю транзакції