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?
źródło
Odpowiedzi:
Aby zdefiniować obowiązki usługi , najpierw musisz zdefiniować, czym jest usługa .
Usługa nie jest kanonicznym ani ogólnym terminem na oprogramowanie. W rzeczywistości sufiks
Service
nazwy klasy jest podobny do bardzo złośliwego Menedżera : nie mówi prawie nic o tym, co faktycznie robi obiekt .W rzeczywistości usługa powinna być wysoce specyficzna dla architektury:
W tradycyjnej architekturze warstwowej usługa jest dosłownie synonimem warstwy logiki biznesowej . To warstwa między interfejsem użytkownika a danymi. Dlatego wszystkie reguły biznesowe dotyczą usług. Warstwa danych powinna rozumieć tylko podstawowe operacje CRUD, a warstwa interfejsu użytkownika powinna zajmować się tylko mapowaniem DTO prezentacji do i od obiektów biznesowych.
W architekturze rozproszonej w stylu RPC (SOAP, UDDI, BPEL itp.) Usługa jest logiczną wersją fizycznego punktu końcowego . Zasadniczo jest to zbiór operacji, które opiekun chce udostępnić jako publiczny interfejs API. Różne przewodniki po najlepszych praktykach wyjaśniają, że operacja usługowa powinna w rzeczywistości być operacją na poziomie biznesowym, a nie CRUD, i raczej się zgadzam.
Ponieważ jednak przekierowywanie wszystkiego przez rzeczywistą usługę zdalną może poważnie zaszkodzić wydajności, zwykle najlepiej nie jest, aby te usługi faktycznie wdrażały logikę biznesową; zamiast tego powinni zawinąć „wewnętrzny” zestaw obiektów biznesowych. Jedna usługa może obejmować jeden lub kilka obiektów biznesowych.
W architekturze MVP / MVC / MVVM / MV * usługi w ogóle nie istnieją. A jeśli tak, termin ten odnosi się do dowolnego obiektu ogólnego, który można wstrzyknąć do kontrolera lub modelu widoku. Logika biznesowa jest w twoim modelu . Jeśli chcesz tworzyć „obiekty usług” w celu koordynowania skomplikowanych operacji, jest to postrzegane jako szczegół implementacji. Niestety wiele osób wdraża MVC w ten sposób, ale jest to uważane za anty-wzorzec ( Anemic Domain Model ), ponieważ sam model nic nie robi, to tylko garść właściwości dla interfejsu użytkownika.
Niektórzy ludzie błędnie myślą, że przyjęcie 100-liniowej metody kontrolera i przeniesienie jej do usługi w jakiś sposób poprawia architekturę. Naprawdę nie; dodaje tylko kolejną, prawdopodobnie niepotrzebną warstwę pośrednią. Praktycznie rzecz biorąc, kontroler nadal wykonuje pracę, po prostu robi to przez źle nazwany obiekt „pomocnika”. Bardzo polecam prezentację Jimmy'ego Bogarda Wicked Domain Models, aby uzyskać jasny przykład, jak zmienić anemiczny model domeny w przydatny. Polega ona na dokładnym zbadaniu modeli, które ujawniasz i które operacje są faktycznie ważne w kontekście biznesowym .
Na przykład, jeśli baza danych zawiera Zamówienia i masz kolumnę Kwota całkowita, prawdopodobnie aplikacja nie powinna mieć możliwości zmiany tego pola na dowolną wartość, ponieważ (a) to historia i (b) powinna to być zależy od tego, co jest w kolejności, a także może od innych danych / reguł wrażliwych na czas. Utworzenie usługi do zarządzania zamówieniami niekoniecznie rozwiązuje ten problem, ponieważ kod użytkownika nadal może pobrać rzeczywisty obiekt zamówienia i zmienić kwotę na nim. Zamiast tego samo zamówienie powinno być odpowiedzialne za zapewnienie, że można je zmienić tylko w bezpieczny i spójny sposób.
W DDD usługi są przeznaczone specjalnie do sytuacji, gdy masz operację, która nie należy właściwie do żadnego zagregowanego katalogu głównego . Musisz być tutaj ostrożny, ponieważ często potrzeba usługi może sugerować, że nie użyłeś właściwych korzeni. Ale zakładając, że to zrobiłeś, usługa służy do koordynowania operacji w wielu katalogach głównych, a czasem do rozwiązywania problemów, które w ogóle nie dotyczą modelu domeny (np. Zapisywanie informacji w bazie danych BI / OLAP).
Ważnym aspektem usługi DDD jest to, że można używać skryptów transakcyjnych . Podczas pracy z dużymi aplikacjami bardzo prawdopodobne jest, że w końcu natkniesz się na instancje, w których łatwiej jest osiągnąć coś za pomocą procedury T-SQL lub PL / SQL niż w przypadku modelu domeny. To jest OK i należy do Usługi.
Jest to radykalne odejście od definicji usług w architekturze warstwowej. Warstwa usługi obudowuje obiekty domeny; usługa DDD hermetyzuje to, czego nie ma w obiektach domeny i nie ma sensu.
W architekturze zorientowanej na usługi , usługa jest uważana za organ techniczny dla zdolności biznesowej. Oznacza to, że jest wyłącznym właścicielem określonego podzbioru danych biznesowych i nic innego nie może dotykać tych danych - nawet po prostu je czytać .
Z konieczności usługi są w rzeczywistości kompleksową propozycją w SOA. Oznacza to, że usługa nie jest tak konkretnym składnikiem, jak cały stos , a cała aplikacja (lub cała firma) to zestaw tych usług, które działają obok siebie bez żadnych przecięć oprócz warstw przesyłania komunikatów i interfejsu użytkownika. Każda usługa ma własne dane, własne reguły biznesowe i własny interfejs użytkownika. Nie muszą się ze sobą aranżować, ponieważ mają być dostosowane do biznesu - i podobnie jak sama firma, każda usługa ma swój własny zestaw obowiązków i działa mniej więcej niezależnie od innych.
Tak więc, zgodnie z definicją SOA, każda logika biznesowa w dowolnym miejscu jest zawarta w usłudze, ale także cały system . Usługi w SOA mogą zawierać składniki i mogą mieć punkty końcowe , ale nazwanie dowolnego fragmentu kodu usługą jest dość niebezpieczne, ponieważ jest ono sprzeczne z tym, co ma oznaczać oryginalne „S”.
Ponieważ SOA jest na ogół bardzo chętna do przesyłania wiadomości, operacje, które mogły być wcześniej spakowane w usłudze, są na ogół zawarte w modułach obsługi , ale ich wielość jest inna. Każdy moduł obsługi obsługuje jeden typ komunikatu, jedną operację. Jest to ścisła interpretacja zasady pojedynczej odpowiedzialności , ale zapewnia doskonałą konserwację, ponieważ każda możliwa operacja należy do jej własnej klasy. Tak więc tak naprawdę nie potrzebujesz scentralizowanej logiki biznesowej, ponieważ polecenia reprezentują operacje biznesowe, a nie techniczne.
Ostatecznie, w dowolnej architekturze, którą wybierzesz, będzie jakiś komponent lub warstwa, która ma większość logiki biznesowej. W końcu, jeśli logika biznesowa jest rozrzucona po całym miejscu, to masz po prostu kod spaghetti. Ale to, czy nazwiesz ten komponent usługą , i jak zostanie zaprojektowany pod względem liczby lub wielkości operacji, zależy od twoich celów architektonicznych.
Nie ma dobrej lub złej odpowiedzi, tylko to, co dotyczy twojej sytuacji.
źródło
Co do twojego tytułu , nie sądzę, żeby pytanie miało sens. Model MVC składa się z danych i logiki biznesowej. Stwierdzenie, że logika powinna znajdować się w Serwisie, a nie Model, jest jak powiedzenie: „Pasażer powinien siedzieć na siedzeniu, a nie w samochodzie”.
Z drugiej strony termin „model” jest terminem przeciążonym. Być może nie miałeś na myśli modelu MVC, ale miałeś na myśli model w sensie obiektu przenoszenia danych (DTO). AKA an Entity. O tym mówi Martin Fowler.
Z mojego punktu widzenia Martin Fowler mówi o rzeczach w idealnym świecie. W prawdziwym świecie Hibernacji i JPA (w krainie Java) DTO są bardzo nieszczelną abstrakcją. Chciałbym umieścić moją logikę biznesową w mojej jednostce. Sprawiłoby to, że wszystko było czystsze. Problem polega na tym, że te podmioty mogą istnieć w stanie zarządzanym / buforowanym, który jest bardzo trudny do zrozumienia i stale uniemożliwia twoje wysiłki. Podsumowując moją opinię: Martin Fowler zaleca właściwą drogę, ale ORM nie pozwalają ci tego zrobić.
Myślę, że Bob Martin ma bardziej realistyczną sugestię i podaje ją w tym filmie, który nie jest darmowy . Mówi o utrzymywaniu DTO w logice. Po prostu przechowują dane i przenoszą je na inną warstwę, która jest znacznie bardziej obiektowa i nie korzysta bezpośrednio z DTO. Dzięki temu unika się nieszczelnej abstrakcji. Warstwa z DTO i same DTO nie są OO. Ale kiedy wyjdziesz z tej warstwy, możesz stać się takim OO, jak zaleca Martin Fowler.
Korzyścią z tego oddzielenia jest to, że oddziela warstwę trwałości. Możesz przełączyć się z JPA na JDBC (lub odwrotnie) i żadna logika biznesowa nie musiałaby się zmieniać. Zależy to tylko od DTO, nie ma znaczenia, w jaki sposób te DTO się zapełniają.
Aby nieznacznie zmienić tematy, należy wziąć pod uwagę fakt, że bazy danych SQL nie są obiektowe. Ale ORM zwykle mają byt - który jest obiektem - na tabelę. Od samego początku przegrałeś już bitwę. Z mojego doświadczenia wynika, że nigdy nie możesz reprezentować Istoty dokładnie tak, jak chcesz w sposób obiektowy.
Jeśli chodzi o „ na służbie”, Bob Martin byłoby sprzeczne posiadające klasę o nazwie
FooBarService
. To nie jest obiektowe. Czym zajmuje się usługa? Wszystko związane zFooBars
. Równie dobrze może być oznaczonyFooBarUtils
. Myślę, że byłby zwolennikiem warstwy usług (lepsza nazwa to warstwa logiki biznesowej), ale każda klasa w tej warstwie miałaby sensowną nazwę.źródło
Obecnie pracuję nad projektem greenfield i wczoraj musieliśmy podjąć kilka decyzji architektonicznych. Co ciekawe, musiałem ponownie zapoznać się z kilkoma rozdziałami „Wzorów architektury aplikacji korporacyjnych”.
Oto co wymyśliliśmy:
W rezultacie otrzymujemy:
Klient -> Usługa -> Domena -> Dane
Możemy zastąpić warstwę klienta, usługi lub danych rozsądnym nakładem pracy. Jeśli logika domeny mieszkała w usłudze i zdecydowałeś, że chcesz zastąpić, a nawet usunąć warstwę usługi, musisz przenieść całą logikę biznesową w inne miejsce. Taki wymóg jest rzadki, ale może się zdarzyć.
Powiedziawszy to wszystko, myślę, że jest to bardzo zbliżone do tego, co miał na myśli Martin Fowler
Poniższy schemat ilustruje to całkiem dobrze:
http://martinfowler.com/eaaCatalog/serviceLayer.html
źródło
Jest to jedna z tych rzeczy, które naprawdę zależą od przypadku użycia. Ogólnym celem warstwy usług jest konsolidacja logiki biznesowej. Oznacza to, że kilku kontrolerów może wywoływać tę samą funkcję UserService.MakeHimPay () bez dbania o sposób realizacji płatności. To, co dzieje się w usłudze, może być tak proste, jak modyfikowanie właściwości obiektu lub może wykonywać złożoną logikę związaną z innymi usługami (tj. Wywoływanie usług stron trzecich, wywoływanie logiki sprawdzania poprawności lub nawet po prostu zapisywanie czegoś w bazie danych. )
Nie oznacza to, że musisz usunąć CAŁĄ logikę z obiektów domeny. Czasami sensowniej jest mieć metodę na obiekcie domeny, która wykonuje pewne obliczenia na sobie. W ostatnim przykładzie usługa jest nadmiarową warstwą nad obiektem repozytorium / domeny. Zapewnia niezły bufor przed zmianami wymagań, ale tak naprawdę nie jest konieczny. Jeśli uważasz, że potrzebujesz usługi, spróbuj wykonać prostą logikę „modyfikuj właściwość X na obiekcie Y” zamiast obiektu domeny. Logika klas domen ma tendencję do „obliczania tej wartości z pól”, zamiast ujawniania wszystkich pól za pomocą metod pobierających / ustawiających.
źródło
Najłatwiejszym sposobem zilustrowania, dlaczego programiści unikają umieszczania logiki domeny w obiektach domeny, jest to, że zwykle mają do czynienia z sytuacją „gdzie umieścić logikę sprawdzania poprawności?”. Weźmy na przykład ten obiekt domeny:
Mamy więc podstawową logikę sprawdzania poprawności w seterach (nie może być ujemna). Problem polega na tym, że tak naprawdę nie można ponownie użyć tej logiki. Gdzieś jest ekran, model ViewModel lub kontroler, który musi przeprowadzić weryfikację, zanim faktycznie zatwierdzi zmianę w obiekcie domeny, ponieważ musi poinformować użytkownika przed kliknięciem przycisku Zapisz, że nie może tego zrobić, i dlaczego . Testowanie wyjątku podczas wywoływania setera jest brzydkim hackem, ponieważ naprawdę powinieneś był wykonać całą weryfikację jeszcze przed rozpoczęciem transakcji.
Dlatego ludzie przenoszą logikę sprawdzania poprawności do jakiejś usługi, takiej jak
MyEntityValidator
. Wtedy jednostka i logika wywołująca mogą zarówno uzyskać odwołanie do usługi sprawdzania poprawności, jak i ponownie z niej korzystać.Jeśli tego nie zrobisz i nadal chcesz ponownie użyć logiki sprawdzania poprawności, ostatecznie umieścisz ją w metodach statycznych klasy encji:
To sprawiłoby, że Twój model domeny byłby mniej „anemiczny” i zachowałby logikę sprawdzania poprawności obok właściwości, co jest świetne, ale nie sądzę, aby ktokolwiek naprawdę lubił metody statyczne.
źródło
Myślę, że odpowiedź jest jasna, jeśli czytasz artykuł Anemic Domain Model Martina Fowlera .
Usunięcie logiki biznesowej, którą jest domena, z modelu domeny jest w istocie łamaniem projektowania obiektowego.
Przyjrzyjmy się najbardziej podstawowej koncepcji obiektowej: Obiekt hermetyzuje dane i operacje. Na przykład zamknięcie konta to operacja, którą obiekt konta powinien wykonać na sobie; dlatego zlecenie wykonania warstwy usługowej nie jest rozwiązaniem zorientowanym obiektowo. Ma charakter proceduralny i do tego odnosi się Martin Fowler, gdy mówi o anemicznym modelu domeny.
Jeśli warstwa usługi zamyka konto, a nie sam obiekt zamknięcia, nie masz rzeczywistego obiektu konta. „Obiekt” twojego konta jest jedynie strukturą danych. To, co kończysz, jak sugeruje Martin Fowler, to kupa torebek z getterami i seterami.
źródło
Jak zaimplementowałbyś logikę biznesową w warstwie usług? Dokonując płatności od użytkownika, tworzysz płatność, a nie tylko odejmujesz wartość od nieruchomości.
Twoja metoda dokonywania płatności musi utworzyć rekord płatności, zwiększyć zadłużenie tego użytkownika i zachować to wszystko w repozytoriach. Wykonanie tego w metodzie serwisowej jest niezwykle proste, a całą transakcję można również zawrzeć w transakcji. Robienie tego samego w zagregowanym modelu domeny jest znacznie bardziej problematyczne.
źródło
Wersja tl; dr:
Moje doświadczenia i opinie mówią, że wszelkie obiekty, które mają logikę biznesową, powinny być częścią modelu domeny. Model danych prawdopodobnie nie powinien mieć żadnej logiki. Usługi powinny prawdopodobnie połączyć te dwa elementy i zająć się zagadnieniami przekrojowymi (bazy danych, rejestrowanie itp.). Jednak zaakceptowana odpowiedź jest najbardziej praktyczna.
Dłuższa wersja, do której nawiązywali inni, polega na tym, że słowo „model” jest niejednoznaczne. Post przełącza się między modelem danych a modelem domeny tak, jakby były takie same, co jest bardzo częstym błędem. Słowo „usługa” może być również nieco niejednoznaczne.
W praktyce nie powinieneś mieć usługi, która wprowadza zmiany w jakichkolwiek obiektach domeny; powodem tego jest to, że twoja usługa prawdopodobnie będzie miała jakąś metodę dla każdej właściwości na twoim obiekcie, aby zmienić wartość tej właściwości. Jest to problem, ponieważ wtedy, jeśli masz interfejs dla swojego obiektu (lub nawet jeśli nie), usługa przestaje być zgodna z zasadą otwartego-zamkniętego; zamiast tego za każdym razem, gdy dodasz więcej danych do swojego modelu (niezależnie od domeny w porównaniu do danych), w końcu będziesz musiał dodać więcej funkcji do swojej usługi. Istnieją pewne sposoby na obejście tego, ale jest to najczęstszy powód, dla którego widziałem, że aplikacje „korporacyjne” zawodzą, szczególnie gdy te organizacje myślą, że „przedsiębiorstwo” oznacza „mieć interfejs dla każdego obiektu w systemie”. Czy możesz sobie wyobrazić dodanie nowych metod do interfejsu, następnie do dwóch lub trzech różnych implementacji (jednej w aplikacji, próbnej implementacji i debugowania jednej, w pamięci?), tylko dla jednej właściwości w twoim modelu? Brzmi dla mnie jak okropny pomysł.
Jest tutaj dłuższy problem, w który nie będę wchodził, ale sedno jest następujące: Hardcore programowania obiektowego mówi, że nikt poza odpowiednim obiektem nie powinien być w stanie zmienić wartości właściwości w obiekcie, ani nawet „ patrz „wartość właściwości w obiekcie. Można to złagodzić, czyniąc dane tylko do odczytu. Nadal możesz napotykać problemy, na przykład gdy wiele osób korzysta z danych, nawet jako tylko do odczytu, i musisz zmienić typ tych danych. Możliwe, że wszyscy konsumenci będą musieli się zmienić, aby to uwzględnić. Właśnie dlatego, jeśli tworzysz interfejsy API do użytku przez każdego i wszystkich, odradza się nie mieć publicznych ani nawet chronionych właściwości / danych; to jest właśnie powód, dla którego OOP został wymyślony.
Myślę, że większość odpowiedzi tutaj, oprócz tej oznaczonej jako zaakceptowana, przesłania problem. Ta oznaczona jako zaakceptowana jest dobra, ale nadal czułem potrzebę odpowiedzi i zgodzenia się, że ogólnie rzecz biorąc, punkt 4 to droga.
źródło
Odpowiedź jest taka, że zależy to od przypadku użycia. Ale w większości ogólnych scenariuszy przestrzegałbym logiki biznesowej układanej w warstwie usługowej. Podany przykład jest naprawdę prosty. Jednak gdy zaczniesz myśleć o oddzielonych systemach lub usługach i dodawać do nich zachowania transakcyjne, naprawdę chcesz, aby stało się to w ramach warstwy usług.
Podane przez ciebie źródła dla warstwy usług pozbawionej logiki biznesowej wprowadzają kolejną warstwę, którą jest warstwa biznesowa. W wielu scenariuszach warstwa usługi i warstwa biznesowa są skompresowane w jedną. To naprawdę zależy od tego, jak chcesz zaprojektować swój system. Możesz wykonać pracę w trzech warstwach i nadal ozdabiać i dodawać hałasu.
To, co możesz idealnie zrobić, to modelowe usługi, które obejmują logikę biznesową do pracy na modelach domen w celu zachowania stanu . Powinieneś postarać się oddzielić usługi tak bardzo, jak to możliwe.
źródło
W MVC Model jest zdefiniowany jako logika biznesowa. Twierdzenie, że powinno być gdzie indziej, jest nieprawidłowe, chyba że nie używa MVC. Widzę warstwy usług jako podobne do systemu modułów. Pozwala ci połączyć zestaw powiązanych funkcji w ładny pakiet. Elementy wewnętrzne tej warstwy usług miałyby model wykonujący taką samą pracę jak Twoja.
źródło
Pojęcie warstwy usługowej można oglądać z perspektywy DDD. Aaronaught wspomniał o tym w swojej odpowiedzi, po prostu trochę go rozwinęłem.
Powszechnym podejściem jest posiadanie kontrolera specyficznego dla danego typu klienta. Powiedzmy, że może to być przeglądarka internetowa, może to być inna aplikacja, może to być test funkcjonalny. Formaty żądań i odpowiedzi mogą się różnić. Dlatego używam usługi aplikacji jako narzędzia do wykorzystania architektury heksagonalnej . Wprowadzam tam klasy infrastruktury specyficzne dla konkretnego żądania. Na przykład tak mógłby wyglądać mój kontroler obsługujący żądania przeglądarki internetowej:
Jeśli piszę test funkcjonalny, chcę użyć fałszywego klienta płatności i prawdopodobnie nie potrzebowałbym odpowiedzi HTML. Mój kontroler mógłby wyglądać tak:
Tak więc usługa aplikacji to środowisko, które skonfigurowałem do uruchamiania logiki biznesowej. To tam nazywane są klasy modeli - niezależnie od implementacji infrastruktury.
Odpowiadając na pytania z tej perspektywy:
Nie.
Można to tak nazwać.
Nie.
Istnieje jednak zupełnie inne podejście, które całkowicie zaprzecza korzystaniu z jakiejkolwiek usługi. Na przykład David West w swojej książce Object Thinking twierdzi, że każdy obiekt powinien mieć wszystkie zasoby niezbędne do wykonywania swojej pracy. Takie podejście powoduje na przykład odrzucenie dowolnej ORM .
źródło
Dla przypomnienia.
SRP:
W takim przypadku można wykonać następujące czynności:
Jeśli dług nie będzie wymagał pewnych obliczeń:
Jeśli jednak wymaga to pewnych obliczeń:
lub też
Ale także, jeśli obliczenia są wykonywane w warstwie trwałości, wówczas taka procedura przechowywania
W takim przypadku chcemy pobrać użytkownika z bazy danych i zaktualizować dług, powinniśmy to zrobić w kilku krokach (w rzeczywistości dwóch) i nie trzeba go owijać / hermetyzować w funkcji usługi.
A jeśli wymaga to przechowywania, możemy dodać trzeci krok
O proponowanym rozwiązaniu
a) Nie powinniśmy obawiać się, że zostawiliśmy programistę końcowemu, aby napisał kilka zamiast enkapsulować go w funkcji.
b) A jeśli chodzi o interfejs, niektórzy programiści uwielbiają interfejs i są w porządku, ale w kilku przypadkach wcale nie są potrzebni.
c) Celem usługi jest utworzenie jej bez atrybutów, głównie dlatego, że możemy korzystać z funkcji współdzielonych / statycznych. Testowanie jednostkowe jest również łatwe.
źródło
"We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function.
„? Mogę zacytować Lewisa Blacka”, gdyby nie mój koń, nie spędziłbym tego roku na studiach ”