Sytuacja
Wcześniej tego wieczora udzieliłem odpowiedzi na pytanie dotyczące StackOverflow.
Pytanie:
Edycja istniejącego obiektu powinna odbywać się w warstwie repozytorium czy w serwisie?
Na przykład, jeśli mam Użytkownika, który ma dług. Chcę zmienić jego dług. Czy powinienem to zrobić w UserRepository lub w serwisie, na przykład BuyingService, uzyskując obiekt, edytując go i zapisując?
Moja odpowiedź:
Powinieneś pozostawić odpowiedzialność za mutowanie obiektu do tego samego obiektu i użyć repozytorium, aby pobrać ten obiekt.
Przykładowa sytuacja:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Komentarz, który otrzymałem:
Logika biznesowa powinna być naprawdę w usłudze. Nie w modelu.
Co mówi Internet?
To sprawiło, że zacząłem szukać, ponieważ nigdy tak naprawdę (świadomie) nie użyłem warstwy usługi. Zacząłem czytać wzorzec warstwy usługi i wzorzec jednostki pracy, ale jak dotąd nie mogę powiedzieć, że jestem przekonany, że należy użyć warstwy usługi.
Weźmy na przykład ten artykuł Martina Fowlera na temat anty-wzorca anemicznego modelu domeny:
Istnieją obiekty, z których wiele nazwano na cześć rzeczowników w przestrzeni domen, i są one powiązane z bogatymi relacjami i strukturą, jakie mają prawdziwe modele domen. Haczyk pojawia się, gdy spojrzysz na zachowanie i zdasz sobie sprawę, że prawie nie ma żadnego zachowania na tych obiektach, co czyni je niewiele więcej niż workami pobierających i ustawiających. Rzeczywiście często modele te mają reguły projektowania, które mówią, że nie należy umieszczać logiki domeny w obiektach domeny. Zamiast tego istnieje zestaw obiektów usług, które przechwytują całą logikę domeny. Usługi te działają na podstawie modelu domeny i wykorzystują model domeny dla danych.
(...) Logiką, która powinna znajdować się w obiekcie domeny jest logika domeny - sprawdzanie poprawności, obliczenia, reguły biznesowe - jakkolwiek chcesz to nazwać.
Wydawało mi się, że o to właśnie chodzi: opowiadałem się za manipulowaniem danymi obiektu, wprowadzając metody wewnątrz tej klasy, które właśnie to robią. Zdaję sobie jednak sprawę, że tak powinno być, i prawdopodobnie ma to więcej wspólnego z tym, jak te metody są wywoływane (przy użyciu repozytorium).
Miałem również wrażenie, że w tym artykule (patrz poniżej) Warstwa usługi jest bardziej uważana za fasadę, która deleguje pracę do podstawowego modelu, niż rzeczywistą warstwę intensywnie pracującą.
Warstwa aplikacji [jego nazwa dla warstwy usług]: określa zadania, które oprogramowanie ma wykonywać, i kieruje ekspresyjne obiekty domeny do rozwiązywania problemów. Zadania, za które odpowiada ta warstwa, mają znaczenie dla firmy lub są niezbędne do interakcji z warstwami aplikacji innych systemów. Ta warstwa jest utrzymywana cienką warstwą. Nie zawiera reguł biznesowych ani wiedzy, a jedynie koordynuje zadania i deleguje pracę do współpracy obiektów domeny w następnej warstwie. Nie ma stanu odzwierciedlającego sytuację biznesową, ale może mieć stan odzwierciedlający postęp zadania dla użytkownika lub programu.
Które jest tutaj wzmocnione :
Interfejsy serwisowe. Usługi udostępniają interfejs usługi, do którego wysyłane są wszystkie wiadomości przychodzące. Interfejs usługi można traktować jako fasadę, która udostępnia potencjalnym konsumentom logikę biznesową zaimplementowaną w aplikacji (zwykle logikę w warstwie biznesowej).
A tutaj :
Warstwa usługi powinna być pozbawiona jakiejkolwiek aplikacji lub logiki biznesowej i powinna koncentrować się przede wszystkim na kilku problemach. Powinien zawijać połączenia w warstwie biznesowej, tłumaczyć domenę na wspólny język, który mogą zrozumieć Twoi klienci, oraz obsługiwać medium komunikacyjne między serwerem a klientem żądającym.
Jest to poważny kontrast z innymi zasobami, które mówią o warstwie usług:
Warstwa usługi powinna składać się z klas z metodami, które są jednostkami pracy z działaniami należącymi do tej samej transakcji.
Lub druga odpowiedź na pytanie, które już powiązałem:
W pewnym momencie Twoja aplikacja będzie potrzebować logiki biznesowej. Możesz także sprawdzić poprawność danych wejściowych, aby upewnić się, że nie żąda się niczego złego lub nieskutecznego. Ta logika należy do twojej warstwy usług.
"Rozwiązanie"?
Postępując zgodnie ze wskazówkami zawartymi w tej odpowiedzi , opracowałem następujące podejście wykorzystujące warstwę usług:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Wniosek
Wszystko razem niewiele się tutaj zmieniło: kod z kontrolera został przeniesiony do warstwy usług (co jest dobrą rzeczą, więc ma to swoje zalety). Nie wydaje się jednak, aby miało to coś wspólnego z moją pierwotną odpowiedzią.
Zdaję sobie sprawę, że wzorce projektowe to wytyczne, a nie reguły osadzone w kamieniu, które należy wdrożyć, gdy tylko jest to możliwe. Nie znalazłem jednak ostatecznego wyjaśnienia warstwy usługi i tego, jak należy ją traktować.
Czy to jest sposób, aby po prostu wyodrębnić logikę ze sterownika i zamiast tego umieścić ją w usłudze?
Czy ma to stanowić umowę między administratorem a domeną?
Czy powinna istnieć warstwa między domeną a warstwą usługi?
I wreszcie: po oryginalnym komentarzu
Logika biznesowa powinna być naprawdę w usłudze. Nie w modelu.
Czy to jest poprawne?
- Jak mam wprowadzić moją logikę biznesową w usłudze zamiast modelu?