Gdzie wyznaczamy granicę między delegowaniem a enkapsulacją logiki biznesowej? Wydaje mi się, że im więcej delegujemy, tym bardziej stajemy się anemiczni . Delegacja promuje jednak również ponowne użycie i zasadę SUCHEGO. Co zatem należy przekazać i co powinno pozostać w naszych modelach domen?
Weź jako przykład następujące obawy:
Autoryzacja . Czy obiekt domeny powinien być odpowiedzialny za utrzymanie swoich reguł kontroli dostępu (takich jak właściwość CanEdit), czy powinien być delegowany do innego komponentu / usługi odpowiedzialnej wyłącznie za zarządzanie dostępem, np. IAuthorizationService.CanEdit (obiekt)? A może powinna to być kombinacja tych dwóch? Być może obiekt domeny ma właściwość CanEdit, która deleguje do wewnętrznej IAuthorizationService, aby wykonać rzeczywistą pracę?
Validation . Ta sama dyskusja jak powyżej dotyczy walidacji. Kto przestrzega zasad i kto jest odpowiedzialny za ich ocenę? Z jednej strony stan obiektu powinien należeć do tego obiektu, a ważność jest stanem, ale nie chcemy przepisywać kodu używanego do oceny reguł dla każdego obiektu domeny. My mogli używać dziedziczenia w tym przypadku ...
Tworzenie obiektów . Klasa fabryczna a metody fabryczne kontra „nowe” wystąpienie. Jeśli użyjemy oddzielnej klasy fabryki, jesteśmy w stanie izolować i hermetyzować logikę tworzenia, ale kosztem otwarcia stanu naszego obiektu na fabrykę. Można to zarządzać, jeśli nasza warstwa domeny znajduje się w osobnym zestawie, odsłaniając wewnętrznego konstruktora używanego przez fabrykę, ale staje się to problemem, jeśli istnieje wiele wzorców tworzenia. A jeśli cała fabryka zajmuje się wezwaniem właściwego konstruktora, to po co mieć fabrykę?
Metody fabryczne w klasie eliminują problem z otwarciem stanu wewnętrznego obiektu, ale ponieważ są one statyczne, nie jesteśmy w stanie przełamać zależności poprzez wstrzyknięcie interfejsu fabrycznego, tak jak możemy to zrobić przy pomocy osobnej klasy fabrycznej.
Trwałość . Można argumentować, że jeśli nasz obiekt domeny zamierza ujawnić CanEdit podczas delegowania odpowiedzialności za wykonanie kontroli autoryzacji na inną stronę (IAuthorizationService), dlaczego nie zastosować metody Save na naszym obiekcie domeny, która robi to samo? Umożliwiłoby to nam ocenę stanu wewnętrznego obiektu w celu ustalenia, czy operację można wykonać bez przerywania enkapsulacji. Oczywiście wymaga to, abyśmy wstrzyknęli instancję repozytorium do naszego obiektu domeny, który trochę pachnie, więc czy zamiast tego wywołujemy zdarzenie domeny i pozwalamy programowi obsługiwać wykonanie operacji utrwalania?
Widzisz, gdzie idę z tym?
Rockford Lhotka ma świetną dyskusję na temat powodów, dla których zdecydował się pójść drogą Class-in-Charge dla swojego frameworka CSLA, a ja mam trochę historii z tym frameworkiem i mogę zobaczyć jego pomysł na obiekty biznesowe równoległe do obiektów domeny na wiele sposobów. Ale starając się bardziej przyłączyć do dobrych ideałów DDD, zastanawiam się, kiedy współpraca staje się zbyt duża.
Jeśli skończę z usługą IAuthorizationService, IValidator, IFactory i IRepository dla mojego zagregowanego katalogu głównego, co pozostanie? Czy posiadanie metody publikowania, która zmienia stan obiektu z wersji Roboczej na Opublikowaną, wystarcza, aby uznać klasę za obiekt domeny nieanemicznej?
Twoje myśli?
źródło
Odpowiedzi:
Większość zamieszania wydaje się dotyczyć funkcjonalności, która w ogóle nie powinna istnieć w modelu domeny:
Trwałość nigdy nie powinna być w modelu domeny. Nigdy przenigdy. Właśnie dlatego polegasz na typach abstrakcyjnych, takich jak
IRepository
gdyby część modelu kiedykolwiek musiała zrobić coś takiego, jak odzyskanie innej części modelu i użycie wstrzykiwania zależności lub innej podobnej techniki w celu uporządkowania implementacji. Więc skreśl to z płyty.Autoryzacja zasadniczo nie jest częścią modelu domeny, chyba że faktycznie jest częścią domeny, np. Jeśli piszesz oprogramowanie zabezpieczające. Mechanicy, którzy mogą wykonywać to, co w aplikacji, są zwykle obsługiwani na „krawędzi” warstwy biznesowej / domeny, części publiczne, z którymi interfejs użytkownika i elementy integracji mogą rozmawiać - kontroler w MVC, usługi lub sam system przesyłania wiadomości w SOA ... dostajesz obraz.
Fabryki (i zakładam, że masz tu na myśli abstrakcyjne fabryki) nie są wcale takie złe w modelu domenowym, ale prawie zawsze są niepotrzebne. Zwykle masz fabrykę tylko wtedy, gdy wewnętrzna mechanika tworzenia obiektów może się zmienić. Ale masz tylko jedną implementację modelu domeny, co oznacza, że zawsze będzie tylko jeden rodzaj fabryki, która zawsze wywołuje te same konstruktory i inny kod inicjujący.
Możesz mieć „wygodne” fabryki, jeśli chcesz - klasy, które zawierają typowe kombinacje parametrów konstruktora i tak dalej - ale szczerze mówiąc, ogólnie mówiąc, jeśli masz wiele fabryk siedzących w modelu domeny, po prostu marnujesz linie kodu.
Więc kiedy przejdziesz wszystkie te, pozostawia to tylko weryfikację. To jedyna trudna sprawa.
Walidacja jest częścią twojego modelu domeny, ale jest także częścią każdego innego komponentu aplikacji. Twój interfejs użytkownika i baza danych będą miały własne, podobne, ale różne reguły sprawdzania poprawności, oparte na podobnym, ale innym modelu koncepcyjnym. Nie jest tak naprawdę określone, czy obiekty muszą mieć
Validate
metodę, ale nawet jeśli tak, zwykle delegują ją do klasy walidatora (nie interfejsu - sprawdzanie poprawności nie jest abstrakcyjne w modelu domeny, jest fundamentalne).Należy pamiętać, że walidator nadal jest technicznie częścią modelu; nie musi być dołączony do zagregowanego katalogu głównego, ponieważ nie zawiera żadnych danych ani stanów. Modele domenowe są rzeczami konceptualnymi, zwykle fizycznie tłumaczącymi na zespół lub kolekcję zespołów. Nie stresuj się problemem „anemicznym”, jeśli kod delegacji znajduje się w bardzo bliskiej odległości od modelu obiektowego; to wciąż się liczy.
Wszystko to sprowadza się do tego, że jeśli zamierzasz robić DDD, musisz zrozumieć, czym jest domena . Jeśli nadal mówisz o uporczywości i autoryzacji, jesteś na niewłaściwym tropie. Domena reprezentuje stan działania systemu - fizyczne i koncepcyjne obiekty i atrybuty. Wszystko, co nie jest bezpośrednio związane z samymi obiektami i relacjami, nie należy do modelu domeny, kropka.
Zasadniczo, rozważając, czy coś należy do modelu domeny, zadaj sobie następujące pytanie:
„Czy ta funkcjonalność może się kiedykolwiek zmienić z przyczyn czysto technicznych?” Innymi słowy, czy nie wynika to z jakiejkolwiek zauważalnej zmiany w świecie biznesu lub domeny?
Jeśli odpowiedź brzmi „tak”, to nie należy do modelu domeny. To nie jest część domeny.
Istnieje bardzo duża szansa, że pewnego dnia zmienisz infrastrukturę uporczywości i autoryzacji. Dlatego nie są częścią domeny, są częścią aplikacji. Dotyczy to również algorytmów, takich jak sortowanie i wyszukiwanie; nie powinieneś wchodzić i implementować kodu binarnego wyszukiwania w swoim modelu domeny, ponieważ twoja domena zajmuje się jedynie abstrakcyjną koncepcją wyszukiwania, a nie jego działaniem.
Jeśli po usunięciu wszystkich rzeczy, które nie mają znaczenia, okaże się, że model domeny jest naprawdę anemiczny , to powinno to służyć jako całkiem dobra wskazówka, że DDD jest po prostu złym paradygmatem dla twojego projektu.
Niektóre domeny naprawdę są anemiczne. Aplikacje zakładek społecznościowych nie mają tak naprawdę „domeny”, o której można mówić; wszystkie twoje obiekty są w zasadzie tylko danymi bez żadnej funkcjonalności. Z drugiej strony system Sales and CRM ma dość ciężką domenę; kiedy ładujesz
Rate
jednostkę, istnieje uzasadnione oczekiwanie, że możesz faktycznie robić rzeczy z tą stawką, na przykład zastosować ją do ilości zamówienia i sprawić, by obliczyła rabaty ilościowe i kody promocyjne i wszystkie te fajne rzeczy.Obiektów domeny, które po prostu przechowywania danych zwykle nie znaczy, że masz anemiczny model domeny, ale to nie musi oznaczać, że masz stworzył zły projekt - może to po prostu oznaczać, że sama domena jest anemiczne i które powinny być za pomocą inna metodologia.
źródło
Nie. Autoryzacja to problem sam w sobie. Polecenia, które nie byłyby ważne z powodu braku uprawnień, powinny zostać odrzucone przed domeną tak wcześnie, jak to możliwe - co oznacza, że często będziemy nawet chcieć sprawdzić autoryzację potencjalnego polecenia w celu zbudowania interfejsu użytkownika (aby nie nawet pokaż użytkownikowi opcję edycji).
Udostępnianie strategii autoryzacji między warstwami (w interfejsie użytkownika i dalej w usłudze lub module obsługi poleceń) jest łatwiejsze, gdy autoryzacja jest złożona oddzielnie od modelu domeny.
Jedną z trudnych części, które można napotkać, jest autoryzacja kontekstowa, w której polecenie może, ale nie musi, być dozwolone nie tylko na podstawie ról użytkownika, ale także danych / reguł biznesowych.
Powiedziałbym też nie, nie w tej dziedzinie (głównie). Sprawdzanie poprawności odbywa się w różnych kontekstach, a reguły sprawdzania poprawności często różnią się w zależności od kontekstu. Rzadko kiedy istnieje proste, absolutne poczucie ważności lub nieważności, gdy rozważa się dane zawarte w agregacji.
Podobnie jak w przypadku autoryzacji, wykorzystujemy logikę sprawdzania poprawności na różnych warstwach - w interfejsie użytkownika, w usłudze lub module obsługi poleceń itp. Ponownie łatwiej jest zastosować DRY z funkcją sprawdzania poprawności, jeśli jest to osobny komponent. Z praktycznego punktu widzenia sprawdzanie poprawności (szczególnie przy użyciu frameworków) wymaga ujawnienia danych, które w innym przypadku powinny zostać obudowane, i często wymaga dołączenia niestandardowych atrybutów do pól i właściwości. Wolę, aby były na innych klasach niż moje modele domen.
Wolę powielać niektóre właściwości w kilku podobnych klasach niż próbować wymusić wymagania dotyczące struktury sprawdzania poprawności w moich jednostkach. To nieuchronnie kończy bałagan klas jednostek.
Używam jednej warstwy pośredniej. W moich najnowszych projektach jest to polecenie + moduł obsługi do tworzenia czegoś, tj
CreateNewAccountCommand
. Alternatywą może być zawsze użycie fabryki (chociaż może to być niezręczne, jeśli reszta operacji encji jest narażona na działanie klasy usług innej niż klasa fabryki).Generalnie jednak staram się być bardziej elastyczny, wybierając opcje projektowania obiektów.
new
jest łatwe i znane, ale nie zawsze wystarczające. Wydaje mi się, że ważne jest zastosowanie tutaj osądu i umożliwienie różnym częściom systemu korzystania z różnych strategii.To rzadko dobry pomysł; Myślę, że jest wiele wspólnych doświadczeń, które to potwierdzają.
Być może model domeny nie jest właściwym wyborem dla tej części twojej aplikacji.
źródło
OK, proszę. Opróżnię to, mówiąc:
Przedwczesna optymalizacja (w tym projektowanie) często może powodować problemy.
IANMF (nie jestem Martin Fowler);)
Brudną małą tajemnicą jest to, że w przypadku małych projektów (nawet prawdopodobnie średnich), ważna będzie spójność twojego podejścia.
Upoważnienie
Dla mnie Uwierzytelnianie i autoryzacja są zawsze kwestią przekrojową. W moim szczęśliwym małym świecie Java jest to delegowane do Spring Security lub frameworka Apache Shiro.
Sprawdzanie poprawności Dla mnie sprawdzanie poprawności jest częścią obiektu, ponieważ uważam, że jest to określenie, czym jest obiekt.
np. Obiekt samochodowy ma 4 koła (OK, są pewne dziwne wyjątki, ale na razie zignorujmy dziwny samochód 3-kołowy). Samochód po prostu nie jest ważny, chyba że ma 4 (w moim świecie), więc walidacja jest częścią definicji samochodu. To nie znaczy, że nie możesz mieć klas sprawdzania poprawności pomocnika.
W moim szczęśliwym świecie Java używam struktur sprawdzania poprawności Bean i używam prostych adnotacji na większości moich pól Bean. Łatwo jest wtedy zweryfikować obiekt bez względu na to, w jakiej warstwie jesteś.
Tworzenie obiektów
Ostrożnie patrzę na klasy fabryczne. Zbyt często widuję
xyxFactoryFactory
klasę;)Zwykle tworzę
new
obiekt w razie potrzeby, dopóki nie natknę się na przypadek, w którym uzasadnione jest wstrzykiwanie zależności (a ponieważ staram się stosować podejście TDD, pojawia się to częściej niż nie).W moim szczęśliwym świecie Java jest to coraz więcej Guice, ale Wiosna jest tu nadal Królem.
Trwałość
Tak więc ta debata toczy się w kółko i na rondach, a ja zawsze o tym myślę.
Niektórzy twierdzą, że jeśli spojrzysz na obiekt w „czysty” sposób, wytrwałość nie jest podstawową właściwością, to jedynie kwestia zewnętrzna.
Inni uważają, że twoje obiekty domeny domyślnie implementują interfejs „trwały” (tak, wiem, że się tutaj rozciągam). Dlatego dobrze jest mieć różne
save
,delete
itp metod na nich. Jest to postrzegane jako podejście pragmatyczne i wiele technologii ORM (JPA w moim szczęśliwym świecie Java) radzi sobie z obiektami w ten sposób.Z przekrojowego zagadnienia bezpieczeństwa upewniam się, że uprawnienia do edycji / usuwania / dodawania / cokolwiek są poprawnie ustawione w usłudze, która wywołuje metodę save / update / delete na obiekcie. Jeśli naprawdę jestem paranoikiem, mogę nawet ustawić uprawnienia do samego obiektu domeny.
HTH!
źródło
Jimmy Nilsson porusza ten temat w swojej książce na temat DDD. Zaczął od modelu anemicznego, poszedł do modeli nieanemicznych w późniejszym projekcie i ostatecznie zdecydował się na modele anemiczne. Rozumował, że modele anemiczne mogą być ponownie wykorzystane w wielu usługach o różnej logice biznesowej.
Kompromisem jest brak zdolności odkrywania. Metody, którymi możesz posługiwać się w modelach anemicznych, są rozpowszechnione w zestawie usług zlokalizowanych gdzie indziej.
źródło
To pytanie zostało zadane dawno temu, ale jest oznaczone tagiem Domain Driven Design. Myślę, że samo pytanie zawiera fundamentalne niezrozumienie całej praktyki, a odpowiedzi, w tym odpowiedź zaakceptowana, utrwalają fundamentalne nieporozumienie.
W architekturze DDD nie ma „modelu domeny”.
Weźmy za przykład Autoryzację. Pozwól, że poproszę o zastanowienie się nad pytaniem: wyobraź sobie, że dwóch różnych użytkowników uwierzytelnia się w twoim systemie. Jeden użytkownik ma uprawnienia do zmiany określonego Podmiotu, a drugi nie. Dlaczego nie?
Nienawidzę prostych i wymyślonych przykładów, ponieważ często mylą bardziej niż oświecają. Ale udawajmy, że mamy dwie różne domeny. Pierwszy to platforma CMS dla agencji marketingowej. Ta agencja ma wielu klientów, którzy mają treści online, którymi muszą zarządzać autorzy tekstów i graficy. Treść obejmuje posty na blogu oraz strony docelowe dla różnych klientów.
Drugą domeną jest zarządzanie zapasami w firmie obuwniczej. System zarządza zapasami od momentu ich dostarczenia od producenta we Francji, do centrów dystrybucyjnych w kontynentalnych Stanach Zjednoczonych, do sklepów detalicznych na lokalnych rynkach, a wreszcie do klienta, który kupuje obuwie w sklepie.
Jeśli uważasz, że zasady autoryzacji są takie same dla obu firm, to tak, byłby to dobry kandydat na usługę spoza domeny. Wątpię jednak, aby zasady autoryzacji były takie same. Nawet koncepcje użytkowników byłyby inne. Z pewnością język byłby inny. Agencja marketingowa prawdopodobnie pełni role autorów postów i właścicieli aktywów, podczas gdy firma obuwnicza prawdopodobnie pełni funkcje takie jak urzędnik wysyłkowy lub kierownik magazynu lub kierownik sklepu.
Pojęcia te prawdopodobnie wiążą się z wszelkiego rodzaju regułami uprawnień, które należy modelować w domenie. Ale to nie znaczy, że wszystkie są częścią tego samego modelu, nawet w tej samej aplikacji. Ponieważ pamiętaj, że istnieją różne powiązane konteksty.
Być może więc można uznać, że nieanemiczny model domeny w kontekście autoryzacji różni się od kontekstu kierowania wysyłek obuwia do sklepów z niskim stanem magazynowym lub kierowania odwiedzających witrynę na odpowiednią stronę docelową w zależności od klikniętej reklamy.
Jeśli znasz anemiczne modele domen, być może po prostu musisz poświęcić więcej czasu na mapowanie kontekstu, zanim zaczniesz pisać kod.
źródło