Jakie jest zastosowanie DTO zamiast Entity?

18

Pracuję nad aplikacją RCP, jestem nowy w tej aplikacji.

Fasolka szparagowa służy do pisania logiki biznesowej w celu zapisywania / pobierania jednostek.

Ale zamiast wysyłać jednostki bezpośrednio do klienta , przechodzimy na DTO i zapełniamy klientów. Podczas oszczędzania ponownie przekształcamy DTO w encję i oszczędzamy.

Jakie są korzyści z tych konwersji? Czy ktoś może wyjaśnić?

Naveen Kocherla
źródło
What's the benefit of these conversions?oddzielenie modelu danych trwałości od modelu danych (reprezentacji) oferowanego konsumentom. Korzyści z oddzielania płatności zostały szeroko omówione w SE Jednak celem pod DTO jest zebranie w jednej odpowiedzi tylu informacji, ile uzna za konieczne, aby klienci mogli zapisać połączenia z serwerem. Co sprawia, że ​​komunikacja klient-serwer jest płynniejsza.
Laiv
Twój przykład jest fajny. Gdy jesteś klientem (widoki ...) zmiana jest bolesna, ale największym problemem jest to, że system ma już integrację trzeciej strony, której nie można zmienić (umowa, opłaty ...). Jeśli twój system będzie miał integrację z innymi firmami, zawsze używaj DTO.
Lucas Gonçalves

Odpowiedzi:

45

Za każdym razem, gdy programista pyta „po co to robić?”, Tak naprawdę ma na myśli „nie widzę przypadku, w którym robienie tego przynosi korzyść”. W tym celu przedstawię kilka przykładów.


Wszystkie przykłady będą oparte na tym prostym modelu danych:

PersonJednostka ma pięć właściwości:Id, FirstName, LastName, Age, CityId

Możesz założyć, że aplikacja korzysta z tych danych na wiele różnych sposobów (raporty, formularze, wyskakujące okienka ...).

Cała aplikacja już istnieje. Wszystko, o czym wspominam, to zmiana istniejącej bazy kodu. Należy o tym pamiętać.


Przykład 1 - Zmiana podstawowej struktury danych - Bez DTO

Wymagania się zmieniły. Wiek osoby musi być dynamicznie pobierany z rządowej bazy danych (załóżmy na podstawie imienia i nazwiska).

Ponieważ nie musisz już przechowywać Agewartości lokalnie, dlatego należy ją usunąć z Personencji. Ważne jest, aby zdać sobie sprawę, że jednostka reprezentuje dane bazy danych i nic więcej. Jeśli nie ma go w bazie danych, nie ma go w encji.
Gdy pobierzesz wiek z rządowego serwisu internetowego, będzie on przechowywany w innym obiekcie (lub int).

Ale twój interfejs nadal wyświetla wiek. Wszystkie widoki zostały skonfigurowane do korzystania z Person.Agewłaściwości, która już nie istnieje. Pojawia się problem: wszystkie widoki odnoszące się do Ageosoby muszą zostać naprawione .


Przykład 2 - Zmiana podstawowej struktury danych - Z DTO

W starym systemie, istnieje również PersonDTOjednostka z tych samych pięciu właściwości: Id, FirstName, LastName, Age, CityId. Po pobraniu a Personwarstwa usługi przekształca ją w a, PersonDTOa następnie zwraca.

Ale teraz wymagania się zmieniły. Wiek osoby musi być dynamicznie pobierany z rządowej bazy danych (załóżmy na podstawie imienia i nazwiska).

Ponieważ nie musisz już przechowywać Agewartości lokalnie, dlatego należy ją usunąć z Personencji. Ważne jest, aby zdać sobie sprawę, że jednostka reprezentuje dane bazy danych i nic więcej. Jeśli nie ma go w bazie danych, nie ma go w encji.

Jednakże, ponieważ masz pośrednika PersonDTO, ważne jest, aby zobaczyć, że klasa ta może utrzymać się Agenieruchomość. Warstwa usługi pobierze Personplik, przekształci go w PersonDTO, a następnie pobierze wiek osoby z rządowego serwisu internetowego, zapisze tę wartość PersonDTO.Agei przekaże ten obiekt.

Ważną kwestią jest to, że każdy, kto korzysta z warstwy usług, nie widzi różnicy między starym a nowym systemem . Obejmuje to twój frontend. W starym systemie otrzymał pełny PersonDTOobiekt. A w nowym systemie nadal otrzymuje pełny PersonDTOobiekt. Widoki nie muszą być aktualizowane .

To mamy na myśli, gdy używamy wyrażenia rozdzielającego obawy : Istnieją dwa różne obawy (przechowywanie danych w bazie danych, prezentacja danych w interfejsie użytkownika) i każdy z nich potrzebuje innego typu danych. Nawet jeśli te dwa typy danych zawierają teraz te same dane, może się to zmienić w przyszłości.
W podanym przykładzie Agejest różnica między dwoma typami danych: Person(jednostka bazy danych) nie potrzebuje Age, ale PersonDTO(typ danych interfejsu użytkownika) jej potrzebuje.
Dzięki oddzieleniu problemów (= tworzenie oddzielnych typów danych) od początku baza kodów jest znacznie bardziej odporna na zmiany wprowadzone w modelu danych.

Możesz argumentować, że posiadanie obiektu DTO po dodaniu nowej kolumny do bazy danych oznacza, że ​​musisz wykonać podwójną pracę, dodając właściwość zarówno do encji, jak i DTO. To technicznie poprawne. Utrzymanie dwóch klas zamiast jednej wymaga nieco więcej wysiłku.

Musisz jednak porównać wymagany wysiłek. Kiedy dodaje się jedną lub więcej nowych kolumn, kopiowanie / wklejanie kilku właściwości nie trwa tak długo. Gdy model danych zmienia się strukturalnie, konieczność zmiany frontendu, być może w sposób powodujący błędy tylko w czasie wykonywania (a nie w czasie kompilacji), wymaga znacznie więcej wysiłku i wymaga od programistów poszukiwania błędów.


Mógłbym podać więcej przykładów, ale zasada zawsze będzie taka sama.

Podsumowując

  • Oddzielne obowiązki (obawy) muszą działać niezależnie od siebie. Nie powinny udostępniać żadnych zasobów, takich jak klasy danych (np. Person)
  • To, że jednostka i jej DTO mają te same właściwości, nie oznacza, że ​​musisz scalić je w tę samą jednostkę. Nie rób zakrętów.
    • Jako bardziej rażący przykład załóżmy, że nasza baza danych zawiera kraje, piosenki i ludzi. Wszystkie te podmioty mają Name. Ale tylko dlatego, że wszystkie mają Namewłaściwość, nie oznacza, że ​​powinniśmy sprawić, aby dziedziczyli po wspólnej EntityWithNameklasie bazowej. Różne Namewłaściwości nie mają znaczącego związku.
    • Jeśli jedna z właściwości kiedykolwiek się zmieni (np. Nazwa utworu Namezostanie zmieniona Titlelub ktoś otrzyma znak „ FirstNamei” LastName), będziesz musiał poświęcić więcej wysiłku, aby cofnąć dziedzictwo, którego nawet nie potrzebowałeś .
    • Chociaż nie jest to tak oczywiste, twój argument, że nie potrzebujesz DTO, gdy masz byt, jest taki sam. Patrzysz na teraz , ale nie przygotowujesz się do przyszłych zmian. JEŚLI jednostka i DTO są dokładnie takie same i JEŻELI możesz zagwarantować, że nigdy nie będzie żadnych zmian w modelu danych; masz rację, że możesz pominąć DTO. Chodzi o to, że nigdy nie można zagwarantować, że model danych nigdy się nie zmieni.
  • Dobra praktyka nie zawsze się opłaca. Może zacząć się opłacać w przyszłości, kiedy będziesz musiał ponownie odwiedzić starą aplikację.
  • Głównym zabójcą istniejących baz kodu jest obniżanie jakości kodu, co coraz bardziej utrudnia utrzymanie bazy kodu, dopóki nie przekształci się w bezużyteczny bałagan kodu spaghetti, którego nie da się utrzymać.
  • Dobra praktyka, taka jak oddzielenie problemów od dotarcia do celu, ma na celu uniknięcie tego śliskiego nachylenia złej konserwacji, aby utrzymać bazę kodów tak długo, jak to możliwe.

Jako ogólną zasadę, aby rozważyć rozdzielenie problemów, pomyśl o tym w ten sposób:

Załóżmy, że wszystkie problemy (interfejs użytkownika, baza danych, logika) są obsługiwane przez inną osobę w innym miejscu. Mogą komunikować się tylko przez e-mail.

W dobrze wyodrębnionej bazie kodu zmiana konkretnego problemu musi być obsługiwana tylko przez jedną osobę:

  • Zmiana interfejsu użytkownika wymaga jedynie tworzenia interfejsu użytkownika.
  • Zmiana metody przechowywania danych wymaga jedynie tworzenia bazy danych.
  • Zmiana logiki biznesowej dotyczy tylko dewelopera biznesowego.

Gdyby wszyscy ci programiści korzystali z tej samej Personjednostki, a do encji wprowadzono niewielką zmianę, wszyscy musieliby być zaangażowani w proces.

Ale dzięki zastosowaniu osobnych klas danych dla każdej warstwy problem ten nie jest tak powszechny:

  • Tak długo, jak deweloper bazy danych może zwrócić prawidłowy PersonDTOobiekt, deweloper biznesowy i interfejs użytkownika nie przejmują się tym, że zmienił sposób przechowywania / pobierania danych.
  • Tak długo, jak deweloper biznesowy przechowuje dane w bazie danych i dostarcza niezbędnych danych do interfejsu użytkownika, deweloperzy bazy danych i interfejsu użytkownika nie dbają o to, czy zdecyduje się przerobić swoje reguły biznesowe.
  • Tak długo, jak interfejs użytkownika można zaprojektować w oparciu o `PersonViewModel, deweloper interfejsu użytkownika może zbudować interfejs użytkownika, jak tylko chce. Deweloperzy baz danych i biznesowi nie dbają o to, jak to się robi, ponieważ nie ma na nie wpływu.

Kluczowym zwrotem jest to, że nie ma na nie wpływu . Wdrożenie dobrego rozdziału problemów ma na celu zminimalizowanie wpływu na inne strony (a zatem konieczność zaangażowania).

Oczywiście, niektóre poważne zmiany nie mogą uniknąć włączenia więcej niż jednej osoby, np. Kiedy do bazy danych dodawany jest zupełnie nowy byt. Ale nie należy lekceważyć ilości drobnych zmian, które należy wprowadzić w trakcie życia aplikacji. Poważne zmiany to mniejszość liczbowa.

Flater
źródło
Kompleksowa odpowiedź, dzięki. Mam pytania; Doceń, jeśli odpowiesz: 1 - Popraw mnie, jeśli się mylę. Obiekt biznesowy lub obiekt widoku służy do przesyłania danych między warstwą prezentacji a warstwą biznesową, a obiekt jednostki służy do przesyłania danych między warstwą biznesową a warstwą dostępu do danych . a DTO może być użyte jako głupi BO. 2 - Załóżmy, że dwa widoki wymagają różnych informacji o firmie, a następnie potrzebujemy dwóch różnych DTO firmy?
Arash,
1
@Arash (1) „DTO” jest naprawdę definicją typu catch-all dla każdej klasy danych używanej do wymiany między dwiema warstwami. Obiekt biznesowy i obiekt widoku są obiektami DTO. (2) To bardzo zależy od wielu rzeczy. Utworzenie nowego dto dla każdej wymaganej kolekcji pól jest uciążliwym zadaniem. Nie ma z natury nic złego w zwróceniu pełnego DTO firmy (tam, gdzie jest to uzasadnione), a następnie umożliwieniu widoku wybrania interesujących go dziedzin. Chodzi o znalezienie równowagi między wdrożeniem odpowiedniego rozdzielenia problemów a unikaniem nadinżynierii i bezcelowego powtarzania.
Flater,
To ma dla mnie sens. Wielkie dzięki. Flater
Arash,
Jeszcze jedno pytanie. Powiedziałeś „obiekt biznesowy i obiekt widoku”. Myślałem, że oba są równe. Kiedy szukałem, zdałem sobie sprawę, że obiekt biznesowy ma ogólne znaczenie w porównaniu z obiektem widoku . Ale obiekt biznesowy powinien pochodzić z przypadku użycia, a obiekt jednostki powinien pochodzić z modelu danych, dlatego są one różne, prawda? czy mógłbyś to trochę wyjaśnić?
Arash,
@Arash: Różnica między tym, co nazywasz „obiektem biznesowym” a „obiektem widoku”, to kontekst . Dla nas ludzi to rozróżnienie ma znaczenie dla właściwego zrozumienia rzeczy. Ale kompilator (a przez to sam język) nie widzi między nimi technicznej różnicy. Kiedy mówię, że są takie same, mam na myśli to z technicznego punktu widzenia. Oba są tylko klasą z właściwościami przeznaczonymi do przechowywania danych i przekazywania. W tym względzie nie ma między nimi żadnej różnicy.
Flater