Widziałem wiele pytań związanych z mapowaniem DTO na obiekty domeny, ale nie czułem, że odpowiedziały na moje pytanie. Używałem już wielu metod i mam własne opinie, ale szukam czegoś bardziej konkretnego.
Sytuacja:
Mamy wiele obiektów domeny. Używamy modelu CSLA, więc nasze obiekty domeny mogą być dość złożone i zawierają własny dostęp do danych. Nie chcesz ich przekazywać na drucie. Będziemy pisać nowe usługi, które będą zwracać dane w wielu formatach (.Net, JSON itp.). Z tego (i innych powodów) tworzymy również niewielki obiekt do przesyłania danych, który będzie przekazywany po kablu.
Moje pytanie brzmi: jak połączyć DTO i obiekt domeny?
Moją pierwszą reakcją jest użycie rozwiązania Fowler, typu DTO . Widziałem to wiele razy i wydaje mi się to właściwe. Obiekt domeny nie zawiera żadnego odniesienia do DTO. Jednostka zewnętrzna („mapper” lub „assembler”) jest wywoływana w celu utworzenia DTO z obiektu domeny. Zwykle jest ORM po stronie obiektu domeny . Wadą tego jest to, że „twórca map” staje się niezwykle złożony w każdej rzeczywistej sytuacji i może być bardzo delikatny.
Innym wysuniętym pomysłem jest to, aby obiekt domeny „zawierał” DTO, ponieważ jest to po prostu ubogi obiekt danych. Właściwości obiektu domeny odwołują się wewnętrznie do właściwości DTO i mogą po prostu zwrócić DTO, jeśli zostanie o to poproszony. Nie widzę z tym żadnych problemów, ale czuję się źle. Widziałem artykuły, w których ludzie używają NHibernate wydają się używać tej metody.
Czy są inne sposoby? Czy warto skorzystać z jednego z powyższych sposobów? Jeśli tak, a jeśli nie, dlaczego?
źródło
Odpowiedzi:
Korzyści z posiadania programu mapującego, który znajduje się między Twoją domeną a DTO, nie są tak oczywiste, gdy obsługujesz tylko jedno mapowanie, ale wraz ze wzrostem liczby mapowań izolowanie tego kodu od domeny pomaga utrzymać prostszą i szczuplejszą domenę. Nie będziesz zaśmiecać swojej domeny zbyt dużą wagą.
Osobiście staram się trzymać mapowanie z dala od jednostek domeny i umieszczam odpowiedzialność na tym, co nazywam „warstwą menedżera / usług”. Jest to warstwa, która znajduje się między aplikacją a repozytorium (ami) i zapewnia logikę biznesową, taką jak koordynacja przepływu pracy (jeśli zmodyfikujesz A, może być konieczne zmodyfikowanie również B, aby usługa A działała z usługą B).
Gdybym miał wiele możliwych formatów końcowych, mógłbym spojrzeć na stworzenie modułu formatującego z możliwością wtyczki, który mógłby używać wzorca Visitor, na przykład do przekształcania moich encji, ale nie znalazłem jeszcze potrzeby na coś tak złożonego.
źródło
Możesz użyć automappera, takiego jak ten napisany przez Jimmy'ego Bogarda, który nie ma połączenia między obiektami i opiera się na przestrzeganiu konwencji nazewnictwa.
źródło
Utrzymywanie logiki mapowania wewnątrz encji oznacza, że obiekt domeny jest teraz świadomy „szczegółów implementacji”, o których nie musi wiedzieć. Ogólnie DTO jest Twoją bramą do świata zewnętrznego (z przychodzącego żądania lub poprzez odczyt z zewnętrznej usługi / bazy danych). Ponieważ encja jest częścią logiki biznesowej, prawdopodobnie najlepiej jest trzymać te szczegóły poza jednostką.
Pozostawienie mapowania gdzie indziej byłoby jedyną alternatywą - ale gdzie to powinno iść? Próbowałem wprowadzić mapowanie obiektów / usług, ale po wszystkim, co zostało powiedziane i zrobione, wydawało się, że jest to nadmierna inżynieria (i prawdopodobnie tak było). Odniosłem pewien sukces, używając Automappera i innych w mniejszych projektach, ale narzędzia takie jak Automapper mają swoje własne pułapki. Miałem dość trudne do znalezienia problemy związane z mapowaniami, ponieważ mapowania Automappera są niejawne i całkowicie oddzielone od reszty twojego kodu (nie jak „oddzielenie problemów”, ale bardziej jak „gdzie działa zapomniane przez Boga mapowanie”), więc oni czasami może być trudno wyśledzić. Nie znaczy to, że Automapper nie ma swoich zastosowań, ponieważ ma. Po prostu uważam, że mapowanie powinno być czymś tak oczywistym i przejrzystym, jak to tylko możliwe, aby uniknąć problemów.
Zamiast tworzyć warstwę usług mapowania, odniosłem duży sukces, utrzymując moje mapowania w moich DTO. Ponieważ DTO zawsze znajduje się na granicy aplikacji, można im uświadomić obiekt biznesowy i dowiedzieć się, jak mapować z / do nich. Nawet jeśli liczba mapowań jest skalowana do rozsądnej ilości, działa to bezproblemowo. Wszystkie mapowania znajdują się w jednym miejscu i nie musisz zarządzać wieloma usługami mapowania w warstwie danych, warstwie antykorupcyjnej lub warstwie prezentacji. Zamiast tego mapowanie jest po prostu szczegółem implementacji delegowanym do DTO zaangażowanego w żądanie / odpowiedź. Ponieważ serializatory generalnie serializują tylko właściwości i pola, gdy wysyłasz je przez sieć, nie powinieneś napotykać żadnych problemów. Osobiście uważam, że jest to najczystsza opcja i mogę powiedzieć, że z mojego doświadczenia
źródło
Używamy szablonów T4 do tworzenia klas mapowania.
Pro's - czytelny dla człowieka kod dostępny w czasie kompilacji, szybszy niż program mapujący w czasie wykonywania. 100% kontrola nad kodem (możliwość wykorzystania częściowych metod / wzorca szablonu w celu rozszerzenia funkcjonalności na zasadzie ad-hoc)
Wady - wykluczenie pewnych właściwości, kolekcji obiektów domeny itp., Nauka składni T4.
źródło
Jak widzisz implementację konstruktora wewnątrz klasy DTO, który przyjmuje jako parametr obiekt domeny?
Powiedz ... Coś w tym stylu
class DTO { // attributes public DTO (DomainObject domainObject) { this.prop = domainObject.getProp(); } // methods }
źródło
DTO
i theDomainObject
, w zasadzie staramy się rozwiązać ten problem DTO zależy od obiektu domeny, więc cała "praca budowania" jest wykonywana w warstwie pośredniej (klasie)mapper
, która jest i DomainObjects. Więc to jest najlepsze lub ogólnie zalecane podejście do tej sprawy? Proszę tylko o upewnienie się, że punkt został zrozumiany.Inne możliwe rozwiązanie: http://glue.codeplex.com .
Funkcje:
źródło
Możesz także wypróbować Otis, mapowanie obiektów do obiektów. Koncepcje są podobne do mapowania NHibernate (atrybut lub XML).
http://code.google.com/p/otis-lib/wiki/GettingStarted
źródło
Mogę zasugerować narzędzie, które stworzyłem i jest open source hostowane w CodePlex: EntitiesToDTOs .
Mapowanie z DTO do Entity i odwrotnie jest realizowane za pomocą metod rozszerzających, które tworzą stronę asemblera na każdym końcu.
Kończysz kodem takim jak:
Foo entity = new Foo(); FooDTO dto = entity.ToDTO(); entity = dto.ToEntity(); List<Foo> entityList = new List<Foo>(); List<FooDTO> dtoList = entityList.ToDTOs(); entityList = dtoList.ToEntities();
źródło
Dlaczego nie możemy tego zrobić?
class UserDTO { } class AdminDTO { } class DomainObject { // attributes public DomainObject(DTO dto) { this.dto = dto; } // methods public function isActive() { return (this.dto.getStatus() == 'ACTIVE') } public function isModeratorAdmin() { return (this.dto.getAdminRole() == 'moderator') } } userdto = new UserDTO(); userdto.setStatus('ACTIVE'); obj = new DomainObject(userdto) if(obj.isActive()) { //print active } admindto = new AdminDTO(); admindto.setAdminRole('moderator'); obj = new DomainObject(admindto) if(obj.isModeratorAdmin()) { //print some thing }
@FrederikPrijck (lub) ktoś: Proszę zasugerować. W powyższym przykładzie DomainObject jest zależny od DTO. W ten sposób mogę uniknąć kodowania mapowania obiektu domeny <--> dto.
lub DomainObject może rozszerzyć klasę DTO?
źródło
Inną opcją byłoby użycie ModelProjector . Obsługuje wszystkie możliwe scenariusze i jest bardzo łatwy w użyciu przy minimalnej powierzchni.
źródło
Możemy do tego użyć wzorca Factory, Memento i Builder. Fabryka ukrywa szczegóły dotyczące tworzenia wystąpienia modelu domeny z DTO. Memento zajmie się serializacją / deserializacją modelu domeny do / z DTO, a nawet może uzyskać dostęp do prywatnych członków. Builder pozwoli na mapowanie z DTO do domeny z płynnym interfejsem.
źródło