Czy dobrą praktyką jest używanie obiektów bytu jako obiektów do przesyłania danych?

29

Zastanawiam się, ponieważ jeśli tak, to dlaczego Entity Framework nie oferuje logiki tworzenia nowego obiektu o tych samych właściwościach do przesyłania danych między warstwami?

Korzystam z obiektów encji, które generuję za pomocą frameworku encji.

użytkownik2484998
źródło
3
Myślę, że to dobre pytanie, ale tak naprawdę nie mogę powiedzieć, ponieważ tak trudno jest je odczytać, ale nie jestem pewien, jak to naprawić. Edytuj swoje pytanie.
candied_orange
1
@CandiedOrange +1, a tym bardziej przerażające jest to, że zyskało tak wiele pozytywnych opinii.
guillaume31

Odpowiedzi:

23

To zależy od Ciebie.

Większość ludzi powie ci, że nie jest to dobra praktyka, ale w niektórych przypadkach możesz jej uniknąć.

EF nigdy nie grał dobrze z DDD z wielu powodów, ale dwa wyróżniają się: nie można sparametryzować konstruktorów na swoich bytach i nie można enkapsulować kolekcji. DDD polega na tym, ponieważ model domeny powinien obejmować zarówno dane, jak i zachowanie.

W pewnym sensie EF zmusza cię do posiadania anemicznego modelu domeny, w tym przypadku możesz użyć encji jako DTO. Możesz napotkać pewne problemy, jeśli korzystasz z właściwości nawigacji, ale możesz szeregować te podmioty i wysyłać je przewodowo. Może to nie być praktyczne. Będziesz musiał kontrolować serializację dla każdego elementu, który ma właściwości, których nie musisz przesyłać. Łatwiejszym sposobem jest zaprojektowanie oddzielnych klas dostosowanych do przesyłania danych. W tym celu tworzone są biblioteki takie jak AutoMapper .

Na przykład: Załóżmy, że masz klasę wywoływaną Personz następującą definicją:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

Zakładając, że chcesz gdzieś wyświetlić listę pracowników, może być praktyczne wysłanie tylko Id, FirstNamei LastName. Ale musisz przesłać wszystkie inne nieistotne właściwości. To nie jest duży problem, jeśli nie zależy ci na wielkości odpowiedzi, ale ogólną ideą jest wysyłanie tylko odpowiednich danych. Z drugiej strony możesz zaprojektować interfejs API, który zwraca listę osób, w takim przypadku może być konieczne przesłanie wszystkich właściwości, co ma sens w przypadku serializacji i wysyłania jednostek. W takim przypadku utworzenie klasy DTO jest dyskusyjne. Niektórzy ludzie lubią mieszać byty i DTO, inni nie.

Aby odpowiedzieć na zaktualizowane pytanie, EF jest ORM. Jego zadaniem jest mapowanie rekordów bazy danych na obiekty i odwrotnie. To, co robisz z tymi obiektami przed i po przejściu przez EF, nie jest częścią jego obaw. Nie powinno tak być.

diabelnie
źródło
1
Krótko podsumowane. Jakie są różnice między DTO a POCO? Czy nie przechowują tylko danych?
Laiv
1
@ guillaume31 Nie możesz mieć prawdziwego modelu domeny bez uwzględnienia uporczywości. Jaki pożytek stanowi interfejs publiczny, jeśli jestem zmuszony użyć refleksji (nie wspominając o innych rodzajach włamań, pod warunkiem, że moja własność publiczna jest wspierana przez prywatne pole o innej nazwie) do ładowania moich agregatów w prawidłowym stanie? Jeśli EF nie zapewnia mi infrastruktury do ukrywania moich agregatów za pomocą ich publicznego interfejsu, lepiej jest zachować logikę biznesową gdzie indziej i mieć anemię mojej domeny zamiast pisać dodatkową płytkę kotłową do ładowania ich z bazy danych.
devnull
1
Czy wolałbyś, aby cały model domeny był anemiczny, niż nazwać publicznie pobierający zbiór w taki sam sposób, jak pole prywatne? (odłóżmy na bok problem konstruktora, ponieważ najczęściej jest to sprzeczne z domeną, aby odsłonić konstruktora zajmującego wszystkie pola obiektu ...)
guillaume31
1
@Konrad stąd : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.Bardzo lubię, aby usługi aplikacyjne były wstrzykiwane moim podmiotom.
devnull
1
@Konrad przestałbym używać moich podmiotów domeny jako DTO (gdybym ich używał w ten sposób). DTO są przydatne niezależnie od wsparcia EF dla wstrzykiwania usługi.
devnull
8

Nie, nie jest.

Idealnie, DTO będą pasować do twoich repozytoriów trwałości (aka, twoich tabel bazy danych).

Ale twoje klasy biznesowe niekoniecznie są do siebie dopasowane. Możesz potrzebować dodatkowych klas lub klas oddzielonych lub połączonych z tym, co masz w bazie danych. Jeśli twoja aplikacja jest mała, możesz nie widzieć tego rodzaju problemów, ale w średnich i dużych aplikacjach zdarza się to często.

Inną rzeczą jest to, że DTO należą do dziedziny, która dotyczy uporczywości, podczas gdy Twoja warstwa biznesowa nie powinna o nich nic wiedzieć.

Juan Carlos Eduardo Romaina Ac
źródło
Obiekt Command to DTO, o którym warstwa biznesowa powinna coś wiedzieć.
devnull
Myślę, że warstwa biznesowa prawdopodobnie wywoła obiekt polecenia pośrednio, poprzez interfejs. Mam na myśli DTO, jak w prostych obiektach, które przenoszą dane, bez żadnej funkcjonalności. Nie jestem pewien, czy obiekt polecenia to DTO ( martinfowler.com/eaaCatalog/dataTransferObject.html ).
Juan Carlos Eduardo Romaina Ac
1
Zazwyczaj polecenia są podzielone na rzeczywiste polecenie i jego moduł obsługi. Polecenie zawiera dane, procedura obsługi zawiera zachowanie. Przy takim użyciu część polecenia zawiera tylko dane otrzymane od klienta i działa jako DTO między klientem a warstwą biznesową.
devnull
DTO nie przepływa od trwałości do domeny, mogą również pochodzić z zewnątrz, jak w nadchodzących danych (pomyśl w interfejsach API REST). Jak wskazuje devnull, polecenia i zdarzenia są w pewnym sensie DTO i są przenoszone do warstwy biznesowej. Jeśli moduły obsługi są warstwą biznesową lub nie, zależy od projektu.
Laiv
4

To właściwie bardzo zły pomysł. Martin Fowler ma artykuł o lokalnych DTO .

Krótko mówiąc, DTOwzorzec był używany do przesyłania danych poza procesem, na przykład przez drut, a nie między warstwami w tym samym procesie.

Constantin Galbenu
źródło
Podobnie jak w przypadku wszystkich podejść programistycznych, nie ma jednego uniwersalnego rozwiązania, w przeciwnym razie nie dyskutowalibyśmy i nie omawialiśmy różnych wzorców i podejść w każdym budowanym przez nas aplecie, po prostu zawsze używaliśmy tych samych rzeczy. DTO to w zasadzie tylko POPO, których nazwano po to, by pokazać pewne zamiary. Nie ma nic z użyciem DTO do „przesyłania danych” między np. Usługą, kontrolerem i na widoku. W nazwie lub strukturze klas nie ma nic, co wskazuje lub ogranicza, skąd i do których dane są przesyłane
James
4

Nie, to zła praktyka.

Pewne powody:

  1. Nowe pola encji będą domyślnie w Dto . Użyj encji oznacza, że ​​każda informacja będzie domyślnie dostępna do konsumpcji. Może to prowadzić do ujawnienia sensownych informacji lub, co najmniej, spowoduje zawyżenie umowy dotyczącej interfejsu API, z dużą ilością informacji, które nie są wykorzystywane dla osób korzystających z interfejsu API. Oczywiście możesz zignorować pola za pomocą adnotacji (np. @JsonIgnoreZe świata Java), ale prowadzi to do następnego problemu ...
  2. Wiele adnotacji / warunków / kontroli na jednostce . Musisz kontrolować, co chcesz wysłać w Dto, gdy zmieni się nazwa atrybutu (spowoduje to zerwanie umowy), dodaj atrybut, który nie pochodzi od encji, kolejność atrybutów itp. Wkrótce zobaczysz swój prosty byt z wieloma adnotacjami, dodatkowymi polami i za każdym razem trudniej będzie zrozumieć, co się dzieje.
  3. Mniejsza elastyczność . Z Dto możesz rozbijać informacje na więcej klas, zmieniać nazwy atrybutów, dodawać nowe atrybuty itp. Nie możesz zrobić tego tak łatwo z jednostką jak Dto.
  4. Niezoptymalizowane . Twoja istota będzie zawsze większa niż zwykłe Dto. Dlatego zawsze będziesz mieć więcej informacji, które chcesz zignorować / zserializować, i prawdopodobnie więcej niepotrzebnych informacji będzie przesyłanych.
  5. Leniwe problemy . Używając encji niektórych frameworków (np. Hibernacji), jeśli spróbujesz odzyskać pewne informacje, które wcześniej nie były leniwie ładowane do transakcji bazy danych, proxy ORM nie zostanie dołączone do transakcji i otrzymasz jakiś „leniwy wyjątek” aby wywołać getmetodę encji.

Tak więc łatwiej i bezpieczniej jest użyć jakiegoś narzędzia mapującego, które pomoże ci w tym zadaniu, mapując pola encji na Dto.

Dherik
źródło
1

Aby zakończyć to, co powiedział @Dherik, głównymi problemami związanymi z używaniem obiektów bytu jako obiektów do przesyłania danych są:

  1. W transakcji podejmujesz ryzyko zatwierdzenia zmian dokonanych w Twojej jednostce, ponieważ używasz jej jako DTO (nawet jeśli możesz odłączyć jednostkę sesji w transakcji, przez większość czasu musisz sprawdzić ten stan przed wszelkie modyfikacje Twojej jednostki-DTO i upewnij się, że nie bierzesz udziału w transakcji lub że sesja została zamknięta, jeśli nie chcesz, aby zmiany były utrwalane).

  2. Rozmiar danych, które udostępniasz między klientem a serwerem: czasami nie chcesz wysyłać całej treści encji do klienta, aby zminimalizować rozmiar odpowiedzi żądania. Oddzielenie DTO od encji jest bardziej elastyczne, aby specjalizować dane, które chcesz wysłać w określonych przypadkach użycia.

  3. Widoczność i konserwacja: Musisz zarządzać adnotacjami jpa / hibernacjami na polach swojej encji i utrzymywać adnotacje Jacksona, aby serializować w jsonie w tym samym miejscu (nawet jeśli możesz oddzielić je od implementacji encji, umieszczając je w interfejsie dziedziczonym przez jednostka). Następnie, jeśli zmienisz treść DTO podczas dodawania nowego pola, inna osoba prawdopodobnie może pomyśleć, że jest to pole encji, a zatem pole danej tabeli w bazie danych (nawet jeśli możesz użyć @Transientadnotacji na wszystkich polach DTO w tym przypadku ...!).

Moim zdaniem powoduje hałas podczas czytania encji, ale moja opinia jest z pewnością subiektywna.

Charles Vuillecard
źródło