Trudno jest ustalić najlepsze praktyki dla czegoś tak „elastycznego” lub abstrakcyjnego jak DTO. Zasadniczo DTO są jedynie obiektami do przesyłania danych, ale w zależności od miejsca docelowego lub przyczyny transferu możesz chcieć zastosować różne „najlepsze praktyki”.
Polecam przeczytać Wzory architektury aplikacji korporacyjnych autorstwa Martina Fowlera . Cały rozdział poświęcony jest wzorcom, w których DTO otrzymują naprawdę szczegółowy rozdział.
Początkowo zostały one „zaprojektowane” do użycia w kosztownych zdalnych połączeniach, w których prawdopodobnie potrzebna byłaby duża ilość danych z różnych części logiki; DTO przesyłałyby dane w jednym połączeniu.
Według autora, DTO nie były przeznaczone do stosowania w lokalnych środowiskach, ale niektóre osoby znalazły dla nich zastosowanie. Zwykle służą do gromadzenia informacji z różnych POCO w jednym obiekcie dla GUI, interfejsów API lub różnych warstw.
Teraz, w przypadku dziedziczenia, ponowne użycie kodu jest bardziej efektem ubocznym dziedziczenia niż jego głównym celem; natomiast kompozycja jest implementowana z ponownym użyciem kodu jako głównym celem.
Niektóre osoby zalecają stosowanie kompozycji i dziedziczenia razem, wykorzystując mocne strony obu i próbując złagodzić swoje słabości. Następujące czynności są częścią mojego procesu mentalnego podczas wybierania lub tworzenia nowych DTO lub jakiejkolwiek nowej klasy / obiektu w tym zakresie:
- Używam dziedziczenia z DTO w tej samej warstwie lub w tym samym kontekście. DTO nigdy nie odziedziczy po POCO, BLL DTO nigdy nie odziedziczy po DAL DTO itp.
- Jeśli próbuję ukryć pole przed DTO, dokonam refaktoryzacji i być może użyję kompozycji.
- Jeśli potrzebuję bardzo niewielu różnych pól z bazowego DTO, umieszczę je w uniwersalnym DTO. Uniwersalne DTO są używane tylko wewnętrznie.
- Podstawowy POCO / DTO prawie nigdy nie będzie używany do żadnej logiki, w ten sposób baza odpowiada tylko potrzebom swoich dzieci. Jeśli kiedykolwiek będę potrzebować skorzystać z bazy, unikam dodawania nowego pola, którego jego dzieci nigdy nie użyją.
Niektóre z nich mogą nie być „najlepszymi” praktykami, działają całkiem dobrze w projektach, nad którymi pracuję, ale musisz pamiętać, że żaden rozmiar nie pasuje do wszystkich. W przypadku uniwersalnego DTO należy zachować ostrożność, moje podpisy metod wyglądają tak:
public void DoSomething(BaseDTO base) {
//Some code
}
Jeśli którakolwiek z metod kiedykolwiek potrzebuje własnego DTO, dziedziczę i zwykle jedyną zmianą, którą muszę wprowadzić, jest parametr, chociaż czasem muszę głębiej szukać konkretnych przypadków.
Z twoich komentarzy wynika, że używasz zagnieżdżonych DTO. Jeśli twoje zagnieżdżone DTO składają się tylko z listy innych DTO, myślę, że najlepszą rzeczą do zrobienia jest rozpakowanie listy.
W zależności od ilości danych, które należy wyświetlić lub z którymi pracować, dobrym pomysłem może być utworzenie nowych DTO, które ograniczają dane; na przykład, jeśli twoje UserDTO ma wiele pól i potrzebujesz tylko 1 lub 2, lepiej mieć DTO tylko z tymi polami. Zdefiniowanie warstwy, kontekstu, użycia i użyteczności DTO bardzo pomoże w jego projektowaniu.