Nie jest złą praktyką, że kontroler bezpośrednio wywołuje repozytorium. „Usługa” to po prostu kolejne narzędzie, więc używaj jej tam, gdzie ma to sens.
NikolaiDante skomentował:
... Wybierz odpowiedni wzór dla właściwej aplikacji. Powiedziałbym, że powinieneś uczynić swoją aplikację spójną.
Nie uważam, że spójność jest najważniejszym aspektem. Klasa „service” ma za zadanie obudować logikę wyższego poziomu, aby sterownik nie musiał jej implementować. Jeśli dla danej operacji nie jest wymagana „logika wyższego poziomu”, wystarczy przejść bezpośrednio do repozytorium.
Aby promować dobrą separację obaw i testowalność, repozytorium powinno być zależnością wprowadzaną do usługi za pośrednictwem konstruktora:
IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);
service.DoSomething(...);
Jeśli wyszukiwanie rekordów w bazie danych wymaga jakiegoś sparametryzowanego zapytania, klasa usług może być dobrym miejscem do wzięcia pod uwagę modelu widoku i zbudowania zapytania, które jest następnie wykonywane przez repozytorium.
Podobnie, jeśli masz złożony model widoku formularza, klasa usług może zawrzeć logikę tworzenia, aktualizowania i usuwania rekordów poprzez wywoływanie metod w modelach / jednostkach domeny, a następnie utrwalanie ich za pomocą repozytorium.
Idąc w przeciwnym kierunku, jeśli kontroler musi uzyskać rekord według swojego identyfikatora, delegowanie do obiektu usługi jest jak uderzenie palcem w młot kowalski - to znacznie więcej niż potrzebujesz.
Odkryłem, że kontroler jest w najlepszej pozycji do obsługi transakcji lub obiektu Unit Of Work . Kontroler lub obiekt Unit Of Work delegowałby następnie do obsługi obiektów dla złożonych operacji lub przechodził bezpośrednio do repozytorium dla prostych operacji (takich jak znalezienie rekordu według 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);
}
}
}
}
Myślę, że połączenie usług i bezpośredniej pracy z repozytoriami jest całkowicie do przyjęcia. Możesz dodatkowo zawrzeć transakcję w obiekcie Unit Of Work, jeśli czujesz taką potrzebę.
Podział obowiązków wygląda następująco:
- Kontroler kontroluje przepływ aplikacji
- Zwraca „404 nie znaleziono”, jeśli koszyka nie ma w bazie danych
- Ponownie renderuje formularz z komunikatami sprawdzania poprawności, jeśli sprawdzanie poprawności się nie powiedzie
- Zapisuje koszyk, jeśli wszystko się sprawdzi
- Kontroler deleguje się do klasy usługi, aby wykonać logikę biznesową na modelach domeny (lub jednostkach). Obiekty usługowe nie powinny implementować logiki biznesowej! Oni wykonać logikę biznesową.
- Administratorzy mogą delegować bezpośrednio do repozytoriów w celu wykonywania prostych operacji
- Obiekty usług pobierają dane w modelu widoku i delegują do modeli domen w celu wykonania logiki biznesowej (np. Obiekt usługi wywołuje metody w modelach domeny przed wywołaniem metod w repozytorium)
- Obiekty usług delegują się do repozytoriów w celu utrwalenia danych
- Administratorzy powinni:
- Zarządzaj czasem życia transakcji lub
- Utwórz obiekt jednostki pracy, aby zarządzać czasem życia transakcji