Dlaczego powinienem izolować jednostki domeny od warstwy prezentacji?

85

Jedną z części projektu opartego na domenie, która wydaje się nie być zbyt szczegółowa, jest sposób i dlaczego należy izolować model domeny od interfejsu. Próbuję przekonać moich kolegów, że to dobra praktyka, ale nie wydaje mi się, aby robiłam duże postępy ...

W warstwach prezentacji i interfejsu używają jednostek domeny, gdziekolwiek chcą. Kiedy argumentuję, że powinni używać modeli wyświetlania lub DTO do izolowania warstwy domeny od warstwy interfejsu, odpowiadają, że nie widzą wartości biznesowej w zrobieniu czegoś takiego, ponieważ teraz masz obiekt interfejsu użytkownika do utrzymania a także oryginalny obiekt domeny.

Dlatego szukam konkretnych powodów, dla których mógłbym to potwierdzić. Konkretnie:

  1. Dlaczego nie powinniśmy używać obiektów domeny w naszej warstwie prezentacji?
    (jeśli odpowiedź jest oczywista: „oddzielenie”, proszę wyjaśnić, dlaczego jest to ważne w tym kontekście)
  2. Czy powinniśmy używać dodatkowych obiektów lub konstrukcji, aby odizolować nasze obiekty domeny od interfejsu?
Mark Rogers
źródło
to pytanie powinno być na wiki.
Syed Tayyab Ali
@ m4bwav - Powinna to być wiki, ponieważ jest sformułowana w taki sposób, aby zachęcić do dyskusji, a nie pojedynczą poprawną odpowiedź.
Rob Allen
1
@ m4bwav: Myślę, że twoje pytanie wyszło bardziej jako opinia niż prawdziwe pytanie ... Próbowałem to poprawić (możesz chcieć to dalej edytować), ale pamiętaj, że bez należytej staranności może się to wydawać trollować.
Shog9
5
OK, kopia zapasowa, zadaję uzasadnione pytanie, jak mogłoby to kogoś urazić? Do kogo jestem skierowany?
Mark Rogers
@ m4bwav: celujesz w swojego słomianego człowieka. „Duża liczba osób”, z którymi omawiasz to w swoim pytaniu.
Shog9

Odpowiedzi:

48

Po prostu powodem jest implementacja i dryf. Tak, Twoja warstwa prezentacji musi wiedzieć o obiektach biznesowych, aby móc je poprawnie przedstawić. Tak, początkowo wygląda na to, że implementacja tych dwóch typów obiektów w dużym stopniu się pokrywa. Problem polega na tym, że w miarę upływu czasu sprawy są dodawane po obu stronach. Prezentacja zmienia się, a potrzeby warstwy prezentacji ewoluują i obejmują elementy całkowicie niezależne od warstwy biznesowej (na przykład kolor). W międzyczasie obiekty domeny zmieniają się w czasie, a jeśli nie masz odpowiedniego oddzielenia od interfejsu, ryzykujesz zepsucie warstwy interfejsu, wprowadzając pozornie łagodne zmiany w obiektach biznesowych.

Osobiście uważam, że najlepszym sposobem podejścia do rzeczy jest ściśle przestrzegany paradygmat interfejsu; oznacza to, że warstwa obiektu biznesowego udostępnia interfejs, który jest jedynym sposobem, w jaki można się z nią komunikować; żadne szczegóły implementacji (tj. obiekty domeny) dotyczące interfejsu nie są ujawniane. Tak, oznacza to, że musisz zaimplementować obiekty domeny w dwóch lokalizacjach; twoją warstwę interfejsu i twoją warstwę BO. Ale ta ponowna implementacja, chociaż początkowo może wydawać się dodatkową pracą, pomaga wyegzekwować oddzielenie, które pozwoli zaoszczędzić TONY pracy w pewnym momencie w przyszłości.

Paul Sonier
źródło
2
Co masz na myśli, mówiąc „zaimplementuj obiekty domeny w dwóch lokalizacjach”?
jlembke
10
Wydaje mi się to po prostu głupie. Po co teraz dodatkowa praca, która MOŻE zaoszczędzić pracę w przyszłości? W 9 przypadkach na 10 nigdy nie będziesz musiał dokonywać zmian, które pozwoliłyby zaoszczędzić „TONY” pracy.
Sygnał dźwiękowy
13
@LuckyLindy: 99 razy na 100 (właściwie więcej), zapinanie pasów bezpieczeństwa nie jest konieczne, aby uniknąć kontuzji. Jednak w jednym przypadku, kiedy naprawdę tego potrzebuję, (prawdopodobnie) uchroni mnie to przed śmiercią lub ciężkimi obrażeniami. Uncja zapobiegania jest warta funta leczenia. Podejrzewam, że Twoja opinia na ten temat ulegnie zmianie po zdobyciu większego doświadczenia.
Paul Sonier
19

Sam się z tym zmagałem. Są przypadki, w których DTO ma sens w prezentacji. Powiedzmy, że chcę wyświetlić listę firm w moim systemie i potrzebuję ich identyfikatora, aby powiązać wartość.

Cóż, zamiast ładować CompanyObject, który może mieć odniesienia do subskrypcji lub kto wie, co jeszcze, mógłbym odesłać DTO z nazwą i identyfikatorem. To jest dobre zastosowanie IMHO.

Weźmy teraz inny przykład. Mam obiekt, który reprezentuje oszacowanie, to oszacowanie może składać się z robocizny, wyposażenia itp., Może mieć wiele obliczeń, które są zdefiniowane przez użytkownika, które biorą wszystkie te pozycje i podsumowują je (każde oszacowanie może być różne dla różnych typów obliczeń). Dlaczego powinienem dwukrotnie modelować ten obiekt? Dlaczego nie mogę po prostu wyliczyć mojego interfejsu użytkownika na obliczeniach i wyświetlić je?

Generalnie nie używam DTO do odizolowania warstwy domeny od interfejsu użytkownika. Używam ich do odizolowania warstwy domeny od granicy, która jest poza moją kontrolą. Pomysł, że ktoś umieści informacje nawigacyjne w swoim obiekcie biznesowym, jest śmieszny, nie zanieczyszczaj tego obiektu biznesowego.

Pomysł, że ktoś umieściłby walidację w swoim obiekcie biznesowym? Cóż, mówię, że to dobra rzecz. Twój interfejs użytkownika nie powinien odpowiadać wyłącznie za weryfikację obiektów biznesowych. Twoja warstwa biznesowa MUSI przeprowadzić własną walidację.

Dlaczego miałbyś umieścić kod generujący interfejs użytkownika w obiekcie busienss? W moim przypadku mam osobne obiekty, które generują kod UI oddzielnie od UI. Mam oddzielne obiekty, które renderują moje obiekty biznesowe w XML, pomysł, że musisz oddzielić warstwy, aby zapobiec tego typu zanieczyszczeniu, jest dla mnie tak obcy, ponieważ po co w ogóle umieszczać kod generujący HTML w obiekcie biznesowym ...

Edycja Myślę, że trochę więcej, ale są przypadki, w których informacje o interfejsie użytkownika mogą znajdować się w warstwie domeny. Może to spowodować zachmurzenie tego, co nazywasz warstwą domeny, ale pracowałem nad aplikacją dla wielu dzierżawców, która miała bardzo różne zachowanie, zarówno w wyglądzie, jak i funkcjonalnym przepływie pracy. W zależności od różnych czynników. W tym przypadku mieliśmy model domeny, który reprezentował dzierżawców i ich konfigurację. Ich konfiguracja zawierała informacje o interfejsie użytkownika (na przykład etykiety dla pól ogólnych).

Gdybym musiał zaprojektować moje obiekty tak, aby były trwałe, czy powinienem również powielić te obiekty? Pamiętaj, że jeśli chcesz dodać nowe pole, masz teraz dwa miejsca na dodanie go. Być może rodzi to kolejne pytanie, czy używasz DDD, czy wszystkie obiekty domeny utrwalonych jednostek? Wiem na moim przykładzie, że tak było.

JoshBerke
źródło
Czy etykiety różniące się dla różnych najemców nie wskazywałyby na inny, wszechobecny język dla każdego najemcy? Myślę, że potrzebna jest koncepcja metamodelu, w którym domena jest współdzielona przez dzierżawców z warstwą translacyjną do ich interpretacji metamodelu.
Kell
16

Robisz to z tego samego powodu, dla którego nie dopuszczasz SQL do stron ASP / JSP.

Jeśli zachowasz tylko jeden obiekt domeny do użytku w warstwie prezentacji ORAZ domenie, ten jeden obiekt wkrótce stanie się monolityczny. Zaczyna zawierać kod weryfikacji interfejsu użytkownika, kod nawigacji interfejsu użytkownika i kod generowania interfejsu użytkownika. Następnie wkrótce dodasz do tego wszystkie metody warstwy biznesowej. Teraz twoja warstwa biznesowa i interfejs użytkownika są pomieszane, a wszystkie z nich bawią się w warstwie encji domeny.

Chcesz ponownie użyć tego fajnego widżetu interfejsu użytkownika w innej aplikacji? Cóż, musisz stworzyć bazę danych o tej nazwie, tych dwóch schematach i tych 18 tabelach. Musisz również skonfigurować Hibernate i Spring (lub wybrane frameworki), aby przeprowadzić weryfikację biznesową. Och, musisz również dołączyć te 85 innych niezwiązanych ze sobą klas, ponieważ są one przywoływane w warstwie biznesowej, która po prostu znajduje się w tym samym pliku.

digitaljoel
źródło
13

Nie zgadzam się.

Myślę, że najlepszym sposobem jest rozpoczęcie od obiektów domeny w warstwie prezentacji, Póki nie ma sensu zrobić inaczej.

Wbrew powszechnemu przekonaniu „obiekty domeny” i „obiekty wartości” mogą szczęśliwie współistnieć w warstwie prezentacji. I to jest najlepszy sposób na zrobienie tego - uzyskujesz korzyści z obu światów, zmniejszoną liczbę duplikatów (i kodu standardowego) z obiektami domeny; oraz dostosowywanie i upraszczanie koncepcyjne używania obiektów wartości w żądaniach.

Daniel Alexiuc
źródło
Dzięki za wkład, widzę, skąd pochodzisz. Chociaż nie mówię, że nie jest to kolejny z nieskończonych sposobów tworzenia udanego projektu, wydaje się, że jest on sprzeczny ze stylem „Domain-Driven Design”, który jest przeznaczony dla większych i bardziej złożonych projektów, które są trudniejsze w utrzymaniu w końcu.
Mark Rogers
Nie, to jest złe i właśnie dlatego tak wiele witryn jest podatnych na wstrzyknięcie sql.
Remi
7

Odpowiedź zależy od skali Twojej aplikacji.


Prosta aplikacja CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie)

W przypadku podstawowych aplikacji Crud nie masz żadnych funkcji. Dodawanie DTO do obiektów byłoby stratą czasu. Zwiększyłoby to złożoność bez zwiększania skalowalności.

wprowadź opis obrazu tutaj


Umiarkowanie skomplikowana aplikacja Non-CRUD

W tej wielkości aplikacji będziesz mieć kilka jednostek, które mają rzeczywisty cykl życia i pewną logikę biznesową z nimi związaną.

Dodanie DTO w tym przypadku jest dobrym pomysłem z kilku powodów:

  • Warstwa prezentacji może wyświetlać tylko podzbiór pól, które posiada jednostka. Hermetyzujesz byty
  • Brak sprzężenia między backendem a frontentem
  • Jeśli masz metody biznesowe wewnątrz jednostek, ale nie w DTO, dodanie DTO oznacza, że ​​zewnętrzny kod nie może zrujnować stanu Twojej jednostki.

wprowadź opis obrazu tutaj


Skomplikowana aplikacja dla przedsiębiorstw

Pojedyncza jednostka może potrzebować wielu sposobów prezentacji. Każdy z nich będzie potrzebował innego zestawu pól. W takim przypadku napotkasz te same problemy, co w poprzednim przykładzie, a ponadto musisz kontrolować liczbę pól widocznych dla każdego klienta. Posiadanie oddzielnego DTO dla każdego klienta pomoże ci wybrać to, co powinno być widoczne.

wprowadź opis obrazu tutaj

Marcin Szymczak
źródło
4

Używamy tego samego modelu na serwerze i na interfejsie użytkownika. I to jest ból. Musimy to kiedyś naprawić.

Problemy wynikają głównie z tego, że model domeny musi zostać pocięty na mniejsze części, aby można było go serializować bez odwoływania się do całej bazy danych. To sprawia, że ​​trudniej jest używać go na serwerze. Brakuje ważnych linków. Niektórych typów nie można również serializować i nie można ich wysłać do klienta. Na przykład „Typ” lub dowolna klasa ogólna. Muszą być nieogólne, a Type należy przesłać jako ciąg. Generuje to dodatkowe właściwości do serializacji, są one zbędne i mylące.

Innym problemem jest to, że jednostki w interfejsie użytkownika tak naprawdę nie pasują. Używamy wiązania danych, a wiele jednostek ma wiele nadmiarowych właściwości tylko do celów interfejsu użytkownika. Ponadto w modelu jednostki istnieje wiele „BrowsableAttribute” i innych. To jest naprawdę złe.

Na koniec myślę, że to tylko kwestia tego, która droga jest łatwiejsza. Mogą być projekty, w których po prostu działa dobrze i nie ma potrzeby pisania kolejnego modelu DTO.

Stefana Steineggera
źródło
2
Jeśli zamierzasz używać wiązania danych, uruchom zapytanie linq i powiąż z typem anonimowym. To pozwala spłaszczyć i zmienić hierarchię. Możesz również bardzo ładnie zaimplementować filtrowanie i sortowanie.
JoshBerke
@Josh: Dzięki za radę. To może częściowo zadziałać. Sam nie jestem programistą GUI i nie zajmuję się zbytnio koncepcjami GUI. Problem będzie występował w przypadkach, gdy dane są manipulowane i wysyłane z powrotem na serwer.
Stefan Steinegger
3

W większości dotyczy zależności. Podstawowa struktura funkcjonalna organizacji ma swoje własne wymagania funkcjonalne, a interfejs użytkownika powinien umożliwiać ludziom modyfikowanie i przeglądanie rdzenia; ale sam rdzeń nie powinien być wymagany do dostosowania interfejsu użytkownika. (Jeśli zajdzie taka potrzeba, zwykle oznacza to, że rdzeń nie został zaprojektowany pod kątem właściwości).

Mój system księgowy ma strukturę i zawartość (oraz dane), które mają modelować działanie mojej firmy. Ta struktura jest prawdziwa i istnieje niezależnie od używanego przeze mnie oprogramowania księgowego. (Nieuchronnie dany pakiet oprogramowania zawiera strukturę i zawartość same w sobie, ale częścią wyzwania jest zminimalizowanie tego narzutu).

Zasadniczo osoba ma pracę do wykonania. DDD powinno odpowiadać przepływowi i treści zadania. DDD polega na jawnym przedstawianiu wszystkich prac, które muszą być wykonane, całkowicie i niezależnie, jak to tylko możliwe. Następnie, miejmy nadzieję, interfejs użytkownika ułatwi wykonanie zadania w możliwie najbardziej przejrzysty i produktywny sposób.

Interfejsy dotyczą danych wejściowych i widoków zapewnianych dla odpowiednio zamodelowanego i niezmiennego rdzenia funkcjonalnego.

dkretz
źródło
3

Cholera, przysięgam , że to powiedział wytrwałość.

W każdym razie jest to jeszcze jeden przykład tego samego: prawo Parnasa mówi, że moduł powinien zachować tajemnicę, a sekret to wymóg, który może się zmienić. (Bob Martin ma regułę, która jest inną wersją tego). W takim systemie prezentacja może się zmieniać niezależnie od domeny . Na przykład firma, która utrzymuje ceny w euro i używa francuskiego w biurach, ale chce przedstawiać ceny w dolarach z tekstem w języku mandaryńskim. Domena jest taka sama; prezentacja może się zmienić. Tak więc, aby zminimalizować kruchość systemu - to znaczy liczbę rzeczy, które trzeba zmienić, aby wprowadzić zmianę w wymaganiach - oddzielasz obawy.

Charlie Martin
źródło
2

Twoja prezentacja może odnosić się do warstwy domeny, ale nie powinno być żadnego powiązania bezpośrednio z interfejsu użytkownika do obiektów domeny. Obiekty domeny nie są przeznaczone do użytku z interfejsem użytkownika, ponieważ często, jeśli są odpowiednio zaprojektowane, są oparte na zachowaniach, a nie na reprezentacjach danych. Pomiędzy interfejsem użytkownika a domeną powinna istnieć warstwa mapowania. MVVM lub MVP jest dobrym wzorem do tego. Jeśli spróbujesz bezpośrednio powiązać swój interfejs użytkownika z domeną, prawdopodobnie sprawisz sobie wiele bólu głowy. Mają dwa różne cele.

jlembke
źródło
1

Być może nie konceptualizujesz warstwy interfejsu użytkownika w wystarczająco ogólny sposób. Pomyśl w kategoriach wielu form odpowiedzi (strony internetowe, odpowiedzi głosowe, drukowane listy itp.) Oraz pod kątem wielu języków (angielski, francuski itp.).

Przypuśćmy teraz, że silnik mowy systemu połączeń telefonicznych działa na zupełnie innym typie komputera (na przykład Mac) niż komputer, na którym działa strona internetowa (być może Windows).

Oczywiście łatwo wpaść w pułapkę „No cóż, w mojej firmie zależy nam tylko na angielskim, prowadzimy naszą stronę na LAMP (Linux, Apache, MySQL i PHP) i wszyscy używają tej samej wersji Firefoksa”. Ale co za 5 lub 10 lat?

JonnyBoats
źródło
1

Z pomocą narzędzia takiego jak „ Value Injecter” ” i koncepcji „Mappers” w warstwie prezentacji podczas pracy z widokami, zrozumienie każdego fragmentu kodu jest znacznie łatwiejsze. Jeśli masz trochę kodu, nie zobaczysz korzyści od razu, ale gdy Twój projekt będzie się rozwijał coraz bardziej, będziesz bardzo zadowolony z pracy z widokami, aby nie musieć wchodzić w logikę usług, repozytoria, aby zrozumieć model widoku. View Model to kolejny strażnik w rozległym świecie warstwy antykorupcyjnej i na wagę złota w długoterminowym projekcie.

Jedynym powodem, dla którego nie widzę korzyści z używania modelu widoku, jest to, że projekt jest mały i wystarczająco prosty, aby widoki były powiązane bezpośrednio z każdą właściwością modelu. Ale jeśli w przyszłości zmieni się wymaganie i niektóre elementy sterujące w widokach nie zostaną przypisane do modelu i nie masz koncepcji modelu widoku, zaczniesz dodawać łatki w wielu miejscach i zaczniesz mieć starszy kod, który nie docenisz. Jasne, możesz dokonać refaktoryzacji, aby przekształcić model widoku w model widoku i postępować zgodnie z zasadą YAGNI, nie dodając kodu, jeśli go nie potrzebujesz, ale dla mnie jest to o wiele bardziej najlepsza praktyka, którą muszę przestrzegać, aby dodać warstwa prezentacji odsłaniająca tylko obiekty modelu widoku.

Samuel
źródło
1

Oto prawdziwy przykład, dlaczego uważam, że dobrą praktyką jest oddzielanie jednostek domeny od widoku.

Kilka miesięcy temu stworzyłem prosty interfejs użytkownika, aby pokazać wartości azotu, fosforu i potasu w próbce gleby za pomocą serii 3 mierników. Każdy miernik miał sekcję czerwoną, zieloną i czerwoną, tj. Można było mieć za mało lub za dużo każdego składnika, ale na środku znajdował się bezpieczny zielony poziom.

Bez większego zastanowienia wymodelowałem swoją logikę biznesową, aby dostarczyć dane dla tych 3 składników chemicznych i oddzielnego arkusza danych, zawierającego dane o akceptowanych poziomach w każdym z 3 przypadków (w tym, która jednostka miary była używana, tj. Mole lub procent). Następnie wymodelowałem swój interfejs użytkownika, aby użyć zupełnie innego modelu, ten model dotyczył etykiet mierników, wartości, wartości granicznych i kolorów.

Oznaczało to, że kiedy później musiałem pokazać 12 komponentów, po prostu zmapowałem dodatkowe dane do 12 nowych modeli widoku miernika i pojawiły się one na ekranie. Oznaczało to również, że mogłem łatwo ponownie użyć kontrolki miernika i wyświetlić inne zestawy danych.

Gdybym połączył te wskaźniki bezpośrednio z jednostkami domeny, nie miałbym żadnej z powyższych elastyczności, a wszelkie przyszłe modyfikacje byłyby bólem głowy. Napotkałem bardzo podobne problemy podczas modelowania kalendarzy w interfejsie użytkownika. Jeśli istnieje wymóg, aby spotkanie w kalendarzu zmieniło kolor na czerwony, gdy jest ponad 10 uczestników, logika biznesowa do obsługi tego powinna pozostać w warstwie biznesowej, a cały kalendarz w interfejsie użytkownika musi wiedzieć, że został poinstruowany, aby zmieni kolor na czerwony, nie powinien wiedzieć dlaczego.

Adrian Thompson Phillips
źródło
-1

Jedynym rozsądnym powodem dodania dodatkowego mapowania między semantyką uogólnioną a semantyką specyficzną dla domeny jest to, że masz (dostęp do) istniejącego zbioru kodu (i narzędzi), które są oparte na uogólnionej (ale dającej się odwzorować) semantyce odmiennej od semantyki domeny.

Projekty oparte na domenie działają najlepiej, gdy są używane w połączeniu z ortogonalnym zestawem funkcjonalnych struktur domeny (takich jak ORM, GUI, przepływ pracy itp.). Zawsze pamiętaj, że semantyka domeny musi być ujawniona tylko w sąsiedztwie warstwy zewnętrznej. Zwykle jest to front-end (GUI) i trwały back-end (RDBM, ORM). Wszelkie skutecznie zaprojektowane warstwy pośrednie mogą i powinny być niezmienne w domenie.

alphazero
źródło
akapit 1: nie twórz niepotrzebnej abstrakcji (np. komponentów wielokrotnego użytku), chyba że faktycznie udostępnisz je w różnych aplikacjach. akapit 2: Zastanawiam się, jak ogólne GUI działają w tak wielu różnych domenach. Uwaga: ta branża jest tak zepsuta, że ​​nie jest już nawet zabawna ...
alphazero