Najlepsze praktyki dotyczące serializacji agregatów DDD

23

Zgodnie z logiką domeny DDD nie należy zanieczyszczać problemami technicznymi, takimi jak serializacja, mapowanie obiektowo-relacyjne itp.

Jak więc serializować lub mapować stan agregatów bez publicznego ujawniania go za pomocą metod pobierających i ustawiających? Widziałem wiele przykładów np. Implementacji repozytoriów, ale praktycznie wszystkie polegały na publicznych akcesoriach na obiektach i obiektach wartości do mapowania.

Możemy użyć refleksji, aby uniknąć publicznych akcesorów, ale IMO te obiekty domenowe nadal będą domyślnie zależeć od problemu serializacji. Np. Nie można zmienić nazwy ani usunąć pola prywatnego bez zmiany konfiguracji serializacji / mapowania. Musisz więc rozważyć serializację, w której powinieneś skupić się na logice domeny.

Jaki jest zatem najlepszy kompromis? Żyjesz z publicznymi akcesoriami, ale unikasz ich używania do celów innych niż kod mapowania? A może po prostu przegapiłem coś oczywistego?

Jestem wyraźnie zainteresowany serializacją stanu obiektów domeny DDD (agregatów składających się z podmiotów i obiektów wartości). Nie chodzi tu o serializację w scenariuszach ogólnych lub skryptach transkacji , w których usługi bezstanowe działają na prostych obiektach kontenera danych.

EagleBeak
źródło

Odpowiedzi:

12

Rodzaje przedmiotów

Na potrzeby naszej dyskusji podzielmy nasze obiekty na trzy różne rodzaje:

Logika domeny biznesowej

Są to obiekty, które wykonują pracę. Przenoszą pieniądze z jednego konta czekowego na drugie, realizują zamówienia i wszystkie inne działania , których realizacji oczekujemy od oprogramowania biznesowego.

Obiekty logiki domeny zwykle nie wymagają akcesoriów (modułów pobierających i ustawiających). Zamiast tego tworzysz obiekt, przekazując zależności przez konstruktor, a następnie manipulujesz nim metodami (powiedz, nie pytaj).

Obiekty przesyłania danych

Obiekty przesyłania danych są stanem czystym; nie zawierają żadnej logiki biznesowej. Zawsze będą mieć akcesoria. Mogą mieć setery, ale nie muszą, w zależności od tego, czy piszesz je w niezmienny sposób. Albo ustawisz swoje pola w konstruktorze, a ich wartości nie zmienią się przez cały okres istnienia obiektu, lub twoje akcesoria będą odczytywane / zapisywane. W praktyce obiekty te zazwyczaj można modyfikować, aby użytkownik mógł je edytować.

Zobacz obiekty modelu

Obiekty modelu widoku zawierają reprezentację danych do wyświetlania / edycji. Mogą zawierać logikę biznesową, zwykle ograniczoną do sprawdzania poprawności danych. Przykładem obiektu View Model może być InvoiceViewModel, zawierający obiekt Customer, obiekt nagłówka faktury i elementy wiersza faktury. Obiekty modelu widoku zawsze zawierają akcesoria.

Zatem jedynym rodzajem obiektu, który będzie „czysty” w tym sensie, że nie będzie zawierał akcesoriów polowych, będzie obiekt Logiki Domeny. Serializacja takiego obiektu zapisuje jego bieżący „stan obliczeniowy”, dzięki czemu można go później odzyskać w celu zakończenia przetwarzania. Wyświetl modele i DTO można dowolnie serializować, ale w praktyce ich dane są zwykle zapisywane w bazie danych.

Serializacja, zależności i sprzężenie

Chociaż prawdą jest, że serializacja tworzy zależności, w tym sensie, że trzeba dokonać deserializacji na zgodny obiekt, niekoniecznie musi to oznaczać konieczność zmiany konfiguracji serializacji. Dobre mechanizmy serializacji są ogólnym celem; nie dbają o to, jeśli zmienisz nazwę właściwości lub członka, o ile nadal może mapować wartości na członków. W praktyce oznacza to tylko, że należy ponownie serializować instancję obiektu, aby reprezentacja serializacji (xml, json, cokolwiek) była zgodna z nowym obiektem; żadne zmiany konfiguracji serializatora nie powinny być konieczne.

Prawdą jest, że obiekty nie powinny przejmować się sposobem ich serializacji. Opisałeś już jeden sposób oddzielenia takich obaw od klas domen: odbicie. Ale serializator powinien martwić się tym, jak serializuje i deserializuje obiekty; taka jest w końcu jego funkcja. Sposób, w jaki trzymasz swoje obiekty oddzielone od procesu serializacji, polega na tym, aby serializacja była funkcją ogólnego przeznaczenia , zdolną do działania na wszystkich typach obiektów.

Jedną z rzeczy, o których ludzie się mylą, jest to, że oddzielenie musi nastąpić w obu kierunkach. To nie; musi działać tylko w jednym kierunku. W praktyce nigdy nie można całkowicie oddzielić; zawsze jest jakieś sprzężenie. Luźne sprzężenie ma na celu ułatwienie konserwacji kodu, a nie usunięcie wszystkich zależności.

Robert Harvey
źródło
Zgadzam się z twoim poglądem na oddzielenie płatności od produkcji. Serializator zależy od obiektu domeny i jest w porządku. Ale nie na odwrót. Nie zgadzam się jednak z twoją opinią na temat publicznych podmiotów przystępujących do obiektów domeny. W praktyce często je mają, tak. Ale IMO lepiej byłoby zaimplementować logikę domeny w czystym projektowaniu obiektowym: powiedz, nie pytaj . Ale nadal potrzebujesz akcesoriów do celów mapowania (ORM, serializacja, GUI ...). I tego chciałbym uniknąć, jeśli to możliwe.
EagleBeak
Jak planujesz uzyskać dostęp do swoich pól, jeśli nie masz dostępu?
Robert Harvey,
W rzeczywistości nie mówię o żadnym z trzech rodzajów obiektów, które opisujesz, ale o „agregacjach” w terminologii DDD i ich obiektach podrzędnych (enity, obiekty wartości). Teraz zdaję sobie sprawę, że moje pytanie nie było wystarczająco jasne na ten temat. Przepraszam! Proszę zobaczyć moją edycję powyżej.
EagleBeak,
1
Jest to w zasadzie nierozwiązany problem - nie można mieć enkapsulacji, odsprzęgania i serializacji \ kodowania jednocześnie ujawniając DTO, to jeden ze sposobów na osiągnięcie kompromisu. Istnieją jednak znacznie mniej inwazyjne sposoby: yegor256.com/2016/07/06/data-transfer-object.html
Basilevs
1
Po opuszczeniu enkapsulacji każdy może zaimplementować lub użyć klasy przyjaciela do odczytania wewnętrznych elementów obiektu.
Basilevs,
-1

Podstawowym celem serializacji jest zapewnienie, że dane wytworzone przez jeden system mogą być wykorzystywane przez jeden lub więcej kompatybilnych systemów.

Najłatwiejszym i najbardziej niezawodnym podejściem do serializacji jest przełożenie danych na format agnostyczny typu, który utrzymuje strukturę w prostym i łatwym w obsłudze formacie. Na przykład najbardziej rozpowszechnione formaty serializacji (tj. JSON, XML) używają dobrze zdefiniowanego formatu tekstowego. Tekst jest prosty w produkcji, transmisji i zużyciu.

Są dwa powody, dla których użycie jednego z tych formatów może nie być idealne.

  1. Wydajność

    Tłumaczenie wszystkich danych na ich tekstowe odpowiedniki wiąże się z nieodłącznym kosztem. Typy danych nie istniałyby, gdyby tekst był najbardziej wydajnym sposobem wyrażania wszystkich różnych form danych. Ponadto struktura tych formatów nie jest idealna do wyszukiwania podzbiorów danych asynchronicznie lub w częściach.

    Na przykład XML i JSON zakładają, że używane dane będą zapisywane i odczytywane od początku do końca. W przypadku przetwarzania bardzo dużych zestawów danych, w których brakuje pamięci, system zużywający dane może wymagać możliwości przetwarzania danych w częściach. W takim przypadku może być wymagana implementacja serializacji / deserializacji specjalnego przeznaczenia do obsługi danych.

  2. Precyzja

    Rzutowanie wymagane do serializacji / deserializacji danych z zamierzonego typu na typ agnostyczny danych powoduje utratę precyzji.

Można argumentować, że tworzenie binarnej reprezentacji obiektów i danych jest zdecydowanie najbardziej wydajnym i dokładnym rozwiązaniem. Główną wadą jest to, że wdrożenie wszystkich systemów, które zarówno zużywają, jak i wytwarzają dane, muszą pozostać kompatybilne. Teoretycznie jest to proste ograniczenie, ale w praktyce jest to koszmar, ponieważ systemy produkcyjne z czasem się zmieniają / ewoluują.

To powiedziawszy. Oddzielenie serializacji / deserializacji od szczegółów specyficznych dla domeny ma sens jako ogólna zasada, ponieważ formaty ogólnego przeznaczenia są bardziej niezawodne, lepiej obsługiwane w różnych systemach i wymagają niewielkiego lub całkowitego zwiększenia nakładów związanych z konserwacją.

Evan Plaice
źródło
Przepraszam, ale to nie odpowiada na moje pytanie. Chodzi o oddzielenie obiektów domeny od serializacji, a nie o przyczyny serializacji lub plusy i minusy różnych formatów. Jak serializować obiekty domeny bez publicznego ujawniania ich stanu prywatnego?
EagleBeak
@EagleBeak Och, nie zdawałem sobie sprawy, że twoja troska dotyczy konkretnie obsługi członków prywatnych. W twoim przypadku możesz dokonać serializacji binarnej (zakładając, że system odbierający przestrzega tych samych reguł / struktur, w których obiekty domeny zostały utworzone) lub napisać logikę, która wyodrębnia tylko dane publiczne przed serializacją.
Evan Plaice,
Myślę, że „zwykłym” założeniem jest to, że dane serializowane do formatu ogólnego przeznaczenia (np. Xml, json) będą publiczne i że uprawnienie jest kontrolowane za pośrednictwem interfejsu API za pośrednictwem list ACL lub innego równoważnego. Serializacja / deserializacja ogólnego przeznaczenia przebiega bardziej podobnie do oddzielania danych od logiki biznesowej, przechodząc z jednego systemu do drugiego.
Evan Plaice,
Zgadzam się z tym, że publiczne obiekty przystępujące są zwykle zakładane na obiekty, które mają być serializowane. Ale nadal chciałbym dowiedzieć się więcej na temat tego, w jaki sposób odnosi się to do DDD i jego silnego nacisku na enkapsulację logiki domeny. Czy wszyscy praktykujący DDD po ​​prostu ujawniają stan modelu domeny za pośrednictwem publicznych urządzeń dostępowych do serializacji (i nigdy nie wspominają o tym w swoich przykładach)? Wątpię. Nie zrozum mnie źle. Bardzo doceniam twój wkład. Po prostu interesuje mnie inny aspekt. (Do tej pory nie sądzę, moje pytanie jest zbyt ogólnikowe, ale i twoja odpowiedź Roberta Harveya dało mi do myślenia o tym ..)
EagleBeak