Obliczone wartości i proste odczyty - dokuczliwy ból dla moich projektów opartych na domenie!

9

Problem, z którym ciągle się spotykam, polega na tym, jak radzić sobie z obliczonymi wartościami opartymi na logice domeny, a jednocześnie skutecznie pracować z magazynem danych.

Przykład:

Zwracam listę Produktów z mojego repozytorium za pośrednictwem usługi. Ta lista jest ograniczona informacjami o paginacji z żądania DTO wysłanego przez klienta. Ponadto DTO określa parametr sortowania (wyliczenie przyjazne dla klienta).

W prostym scenariuszu wszystko działa świetnie: usługa wysyła wyrażenia stronicowania i sortowania do repozytorium, a repozytorium wysyła wydajne zapytanie do bazy danych.

Wszystko się jednak psuje, gdy muszę posortować wartości wygenerowane w pamięci z mojego modelu domeny. Na przykład klasa Product ma metodę IsExpired (), która zwraca wartość logiczną na podstawie logiki biznesowej. Teraz nie mogę sortować i stronicować na poziomie repozytorium - wszystko byłoby zrobione w pamięci (nieefektywne), a moja usługa musiałaby znać zawiłości, kiedy wydawać te parametry repo i kiedy przeprowadzać sortowanie / stronicowanie samo.

Jedynym wzorcem, który wydaje mi się sensowny, jest przechowywanie stanu encji w db (uczyń IsExpired () polem tylko do odczytu i zaktualizuj go za pomocą logiki domeny przed zapisaniem). Jeśli podzielę tę logikę na osobne repozytorium „czytaj model / dto” i „raportowanie”, sprawię, że mój model będzie bardziej anemiczny, niż bym tego chciał.

BTW, każdy przykład, jaki tam widziałem, dla takich obliczeń, naprawdę opiera się na przetwarzaniu w pamięci i glosowaniu z uwagi na fakt, że na dłuższą metę jest znacznie mniej wydajny. Może przedwcześnie optymalizuję, ale to po prostu nie pasuje do mnie.

Chciałbym usłyszeć, jak inni sobie z tym poradzili, ponieważ jestem pewien, że jest to powszechne w prawie projekcie z udziałem DDD.

drogon
źródło

Odpowiedzi:

3

Nie sądzę, że posiadanie dwóch różnych modeli domen tego samego modelu danych powoduje, że Twoja domena jest anemiczna. Anemiczny model domeny to taki, w którym często zmieniająca się logika biznesowa jest ukryta przed domeną w warstwie usługi (lub, co gorsza, w warstwie interfejsu użytkownika).

Rozdzielanie modeli domen poleceń i zapytań jest często zalecane i ma ładny akronim, że możesz google w CQRS (Command Query Responsibility Segregation).

Wykorzystując wzorzec modelu domeny, Udi Dahan

Podczas gdy w przeszłości byłem „sukcesem” w tworzeniu jednego trwałego modelu obiektowego, który obsługiwał zarówno polecenia, jak i zapytania, często bardzo trudno było go skalować, ponieważ każda część systemu szarpała model w innym kierunku.

Okazuje się, że programiści często przyjmują bardziej rygorystyczne wymagania niż firma faktycznie potrzebuje. Decyzja o użyciu encji modelu domeny do wyświetlania informacji użytkownikowi jest właśnie takim przykładem.

[...]

Dla tych, którzy są na tyle starsi, aby pamiętać, najlepsze praktyki korzystania z COM + doprowadziły nas do stworzenia osobnych komponentów dla logiki tylko do odczytu i logiki do odczytu i zapisu. Oto dekadę później nowe technologie, takie jak Entity Framework, ale te same zasady nadal obowiązują.

CQRS z aktorami Akka i funkcjonalnymi modelami domen, Debasish Ghosh

Greg Young wygłosił kilka świetnych sesji na temat DDD i CQRS. W 2008 roku powiedział: „Jeden model nie może być odpowiedni do raportowania, wyszukiwania i zachowań transakcyjnych”. Mamy co najmniej dwa modele - jeden, który przetwarza polecenia i przekazuje zmiany do innego modelu, który obsługuje zapytania i raporty użytkowników. Zachowanie transakcyjne aplikacji jest wykonywane przez bogaty model domenowy agregatów i repozytoriów, a zapytania są obsługiwane bezpośrednio ze zdenormalizowanego modelu danych.

CQRS, Martin Fowler

Zmiana, którą wprowadza CQRS, polega na podzieleniu tego modelu koncepcyjnego na osobne modele do aktualizacji i wyświetlania, które określa się odpowiednio jako Polecenie i Zapytanie zgodnie ze słownictwem CommandQuerySeparation. Uzasadnieniem jest to, że w przypadku wielu problemów, szczególnie w bardziej skomplikowanych domenach, posiadanie tego samego modelu pojęciowego dla poleceń i zapytań prowadzi do bardziej złożonego modelu, który nie radzi sobie dobrze.

Krótko mówiąc, twój pomysł, aby model poleceń obsługiwał wygaśnięcie i przekazywał go do bazy danych, jest w porządku. Przeczytaj pierwszy artykuł powyżej, a zobaczysz podobne, ale bardziej złożone scenariusze.

pdr
źródło
2

SPECYFIKACJA

Wiem, że już zaakceptowałeś odpowiedź, ale zapytałeś o DDD, a dokładne dopasowanie jest tym, co Evans nazywa „specyfikacją”:
bezpośredni link do książek Google
Jeśli ten link nie działa, sprawdź książkę w tych wynikach
Jest to strona 226, jeśli masz książkę.

Na stronie 227 opisano 3 zastosowania specyfikacji: Walidacja, Selekcja, Budowanie nowego obiektu specjalnego. Twój jest „wybór” - IsExpired.

Inną rzeczą związaną z koncepcją „specyfikacji” jest to, że przyznaje ona - ze względu na wydajność - może być potrzebna jedna wersja kodu do działania na obiektach w pamięci, a inna wersja kodu do wydajnego odpytywania repozytorium bez konieczności wcześniejszego uzyskania wszystkie obiekty do pamięci.

W prostym świecie oznaczałoby to umieszczenie wersji SQL w repozytorium i wersji obiektów w modelu, co oczywiście ma wady. Logika znajduje się w 2 miejscach (źle, ktoś zapomni o aktualizacji tych miejsc), aw repozytorium jest logika domeny.

Zatem odpowiedzią jest umieszczenie obu zestawów logiki w specyfikacji. Oczywiście wersja w pamięci, ale także wersja repozytorium. Jeśli używasz na przykład n-hibernacji, możesz użyć jego wbudowanego języka zapytań dla wersji repozytorium.

W przeciwnym razie będziesz musiał utworzyć specjalną metodę repozytorium dla tej specyfikacji, która będzie używana z obiektu specyfikacji. Wywołania kolekcji obiektów zgodnych ze specyfikacją przechodziłyby przez specyfikację, a nie repozytorium. A przynajmniej kod krzyczy: „Jestem w 2 miejscach, nie zapomnij o tym” przyszłym opiekunom. Na stronie 231-232 znajduje się wspaniały przykład rozwiązania bardzo podobnego problemu.

Specyfikacja jest „dozwolonym” wyciekiem / poślizgiem „czystości” DDD. Nadal może nie spełniać twoich potrzeb do różnych celów. Na przykład ORM może generować zły SQL; może być za dużo dodatkowego kodowania. Może więc być konieczne wywołanie metod repozytorium, tak że prawie przypomina to umieszczenie SQL w specyfikacji. Oczywiście zła rzecz. Ale nie zapominaj, że twój program musi działać z rozsądną prędkością. Nie musi wygrywać nagrody czystości DDD. Tak więc rzeczywistość zmiany magazynów danych może oznaczać staromodną chirurgię w całym programie. Również zła rzecz. Ale nie tak zły jak powolny (aka SUCKing) program. Jeśli uruchamianie się po wyjęciu z pudełka na różnych bazach danych jest rzeczywistością, oczywiście powielisz reguły biznesowe dla każdego magazynu danych dla każdej specyfikacji. Przynajmniej masz na to ochotę i możesz skorzystać ze wzorca strategii podczas wymiany repozytoriów. Ale jeśli używasz określonej bazy danych, już pamiętajYAGNI.

W odniesieniu do CQRS: cytat Fowlera autorstwa pdr powyżej nadal obowiązuje tutaj: „posiadanie tego samego modelu koncepcyjnego dla poleceń i zapytań prowadzi do bardziej złożonego modelu, który nie działa dobrze” ... i może być konieczne użycie CQRS lub podobnego. Ale jest znacznie droższy z punktu widzenia rozwoju i utrzymania. Jeśli jesteś dostawcą paczek konkurującym z innymi, może się opłacić. Jeśli piszesz niestandardową aplikację LOB dla jednego klienta, fotografowanie do perfekcji jest kiepskim wyborem. Musisz zdecydować, czy wartość posiadania całkowicie lub w większości podwójnego modelu jest warta dodatkowego wysiłku. Specyfikacjajest dobrym kompromisem, ponieważ pozwala na dokonanie tego rozdziału w zaledwie jednej części programu, która tego potrzebuje, z szybkością (rozwoju) i prostotą jednego modelu. Powodzenia!

FastAl
źródło
To ma sens. Myślę, że muszę ugryźć kulę i przeczytać książkę Evansa :-) Widzę teraz, że płytkie zrozumienie tych pojęć może naprawdę cię sparaliżować!
drogon
0

Chyba zapytałbym, jaka jest logika biznesowa, która określa, czy isExpired jest prawdą, czy nie. Czy tę logikę można wykonać za pomocą zapytania w obecnym modelu danych? Jeśli tak, to czy możesz uczynić swoje repozytorium wystarczająco inteligentnym, aby używać logiki „isExpired”, gdy w określony sposób poprosisz o produkty? Jeśli nie, być może trzeba ponownie zbadać model danych.

DDD nie oznacza, że ​​twoje repozytoria muszą być głupie - to po prostu oznacza, że ​​twoja domena musi wiedzieć, jak rozmawiać z repozytoriami.

Matthew Flynn
źródło