Podejście DDD do podstawowych operacji CRUD w złożonej aplikacji zorientowanej na domenę

9

Moja firma przepisuje naszą aplikację internetową od zera. Jest to aplikacja dla dużych przedsiębiorstw ze złożoną domeną w branży finansowej.

Używamy ORM (Entity Framework) do utrwalania.

Zasadniczo połowa naszych aplikacji skupia się na gromadzeniu nieprzetworzonych danych od użytkownika, przechowywaniu ich, a następnie druga połowa aplikacji, która zawiera większość naszej rzeczywistej logiki domeny, bierze te surowe dane, aby stworzyć nasz obraz domeny, który różni się znacznie od tych oryginalnych surowe dane wejściowe i przekazuje je do silnika obliczeniowego, uruchamia cielęta i wypluwa wyniki, które są następnie wyświetlane użytkownikowi.

W podejściu DDD z wykorzystaniem warstw wydaje się, że operacje CRUD przechodzą przez warstwę domeny. ale przynajmniej w naszym przypadku nie wydaje się to mieć sensu.

Gdy użytkownik przechodzi na przykład do ekranu edycji, aby na przykład zmienić rachunek inwestycyjny, pola na ekranie to dokładne pola przechowywane w bazie danych, a nie reprezentacja domeny używana później do obliczeń. Dlaczego więc miałbym ładować reprezentację domeny rachunku inwestycyjnego, kiedy ekran edycji wymaga reprezentacji bazy danych (surowe dane wejściowe)?

Gdy użytkownik kliknie „Gotowe” na ekranie rachunku inwestycyjnego, a POST zostanie wykonany dla kontrolera, kontroler ma teraz dokładną reprezentację bazy danych rachunku inwestycyjnego, którą musi zapisać. Ale z jakiegoś powodu powinienem załadować reprezentację domeny, aby dokonać modyfikacji, zamiast po prostu mapować model kontrolera bezpośrednio do modelu bazy danych (model szkieletowy Entity)?

Zasadniczo odwzorowuję model danych na model domeny, tak aby można go było odwzorować z powrotem na model danych, aby zachować. W jaki sposób to ma sens?

wired_in
źródło

Odpowiedzi:

9

Ok, wyobraź sobie, że implementujesz stronę tworzenia konta, mapując formularz bezpośrednio na obiekt EF, który jest następnie zapisywany w bazie danych.

Załóżmy ponadto, że baza danych ma różne ograniczenia, które uniemożliwiają wprowadzenie całkowicie błędnych danych. Konta zawsze mają klientów itp.

Wszystko wydaje się działać dobrze. Ale wtedy firma ustanawia nową zasadę.

  • Konta utworzone w czwartek otrzymują premię w wysokości 2%. (załóżmy, że stopa procentowa jest jednym z pól konta)

Teraz musisz gdzieś umieścić tę logikę i nie masz obiektu domeny do jej wprowadzenia.

DDD zakłada, że ​​zawsze będziesz mieć tego rodzaju reguły i prawdopodobnie tak jest. Utworzenie konta musi obejmować różne kontrole, dziennik kontroli itp. Nie będzie to po prostu „zapisz wiersz do bazy danych”

Zaplanuj swoją domenę, zakładając, że nie ma trwałości ani kontrolerów MVC z dodatkową logiką. Upewnij się, że spełniasz wszystkie wymagania i wszystkie są w modelu domeny.

Ewan
źródło
3
To dobry sposób na wyrażenie tego. Nienawidzę znajdowania reguł biznesowych zmieszanych ze szczegółami DB. +1
candied_orange
Dobre punkty, ale co zrobić, jeśli te zasady sprawdzania poprawności mają zastosowanie tylko podczas tworzenia i aktualizacji danych wejściowych użytkownika? Następnie, gdy mamy dane wprowadzone przez użytkownika, model, który jest tworzony podczas wykonywania obliczeń, jest zupełnie innym modelem. Czy powinniśmy mieć dwa modele domen dla rachunku inwestycyjnego? Jeden dla operacji CRUD surowych danych wejściowych dla użytkownika, a drugi dla tych danych wejściowych, które są wykorzystywane do tworzenia modelu domeny używanego w obliczeniach?
wired_in
mylę moje pytania. musisz podać pełny przykład. Jeśli masz logikę domeny, powinna ona przejść do obiektu domeny. To nie znaczy, że nie możesz utworzyć kolejnego obiektu domeny później od pierwszego
Ewan
Wyobraź sobie złożony silnik obliczeniowy. Jednym z danych wejściowych potrzebnych do przeprowadzenia obliczeń jest rachunek inwestycyjny, ale cały rachunek inwestycyjny jest przeznaczony do silnika obliczeniowego i stanowi strumień dochodów przez pewien okres czasu. Ten model domeny rachunku inwestycyjnego jest całkowicie różny od surowych danych wejściowych wprowadzonych przez użytkownika dla tego rachunku inwestycyjnego. Jednak gdy użytkownik wprowadza podstawowe dane wejściowe, takie jak Nazwa, Wartość bieżąca itp., Nadal musi istnieć logika sprawdzania poprawności, ale nie powinna ona mieć nic wspólnego z modelem używanym przez silnik obliczeniowy. Czy istnieją tutaj dwa modele domen dla rachunku inwestycyjnego?
wired_in
..... a może posiadanie modelu rachunku inwestycyjnego w domenie to przesada w operacjach CRUD i powinny być użyte atrybuty walidatora lub coś takiego
wired_in
7

W jaki sposób to ma sens?

Krótka odpowiedź: nie .

Dłuższa odpowiedź: ciężkie wzorce do opracowania modelu domeny nie dotyczą tych części rozwiązania, które są tylko bazą danych.

Udi Dahan miał ciekawe spostrzeżenie, które może pomóc to wyjaśnić

Dahan uważa, że ​​usługa musi mieć zarówno funkcjonalność, jak i pewne dane. Jeśli nie ma danych, jest to tylko funkcja. Jeśli wszystko, co robi, to wykonywanie operacji CRUD na danych, to jest to baza danych.

W końcu model domeny ma zapewnić, że wszystkie aktualizacje danych zachowają bieżący niezmiennik biznesowy. Innymi słowy, model domeny jest odpowiedzialny za zapewnienie poprawności bazy danych, która działa jak system zapisu .

Kiedy masz do czynienia z systemem CRUD, zwykle nie jesteś systemem zapisu danych. Świat rzeczywisty to księga rekordów, a twoja baza danych to tylko lokalna pamięć podręczna reprezentacji świata rzeczywistego.

Na przykład większość informacji pojawiających się w profilu użytkownika, takich jak adres e-mail lub numer identyfikacyjny wydany przez rząd, ma źródło prawdy, które mieszka poza Twoją firmą - to administrator poczty innej osoby przypisuje i odwołuje adresy e-mail, a nie twoja aplikacja. To rząd przypisuje numery SSN, a nie Twoja aplikacja.

Dlatego zwykle nie będziesz przeprowadzać walidacji domen na danych przychodzących do ciebie ze świata zewnętrznego; możesz mieć kontrole, aby upewnić się, że dane są poprawnie sformułowane i odpowiednio zdezynfekowane ; ale to nie twoje dane - Twój model domeny nie otrzymuje weta.

W podejściu DDD z wykorzystaniem warstw wydaje się, że operacje CRUD przechodzą przez warstwę domeny. ale przynajmniej w naszym przypadku nie wydaje się to mieć sensu.

Tak jest w przypadku, gdy baza danych jest księgą rekordów .

Ouarzy tak to ujął .

Pracując nad wieloma starszymi kodami, obserwuję często popełniane błędy w identyfikowaniu tego, co jest w domenie, a co na zewnątrz.

Aplikację można uznać za CRUD tylko wtedy, gdy wokół modelu danych nie ma logiki biznesowej. Nawet w tym (rzadkim) przypadku model danych nie jest modelem domeny. Oznacza to po prostu, że ponieważ nie ma w tym żadnej logiki biznesowej, nie potrzebujemy żadnej abstrakcji, aby nią zarządzać, a zatem nie mamy modelu domeny.

Używamy modelu domeny do zarządzania danymi należącymi do domeny; dane spoza domeny są już zarządzane gdzie indziej - po prostu buforujemy kopię.

Greg Young wykorzystuje systemy magazynowe jako główną ilustrację rozwiązań, w których księga rekordów znajduje się gdzie indziej (np. Podłoga magazynu). Implementacja, którą opisuje, jest bardzo podobna do twojej - jedna logiczna baza danych do przechwytywania wiadomości otrzymanych z magazynu, a następnie osobna logiczna baza danych buforująca wnioski wyciągnięte z analizy tych wiadomości.

Więc może mamy tutaj dwa ograniczone konteksty? Każdy z innym modelem dlainvestment account

Może. Nie chciałbym oznaczać go jako kontekstu ograniczonego, ponieważ nie jest jasne, jaki inny bagaż jest z nim związany. Możliwe, że masz dwa konteksty, może to być jeden kontekst z subtelnymi różnicami we wszechobecnym języku, którego jeszcze nie znasz.

Możliwy test lakmusowy: ilu ekspertów domeny potrzebujesz dwóch ekspertów domeny, aby objąć to spektrum, lub tylko jednego, który mówi o komponentach na różne sposoby. Zasadniczo możesz odgadnąć, ile masz ograniczonych kontekstów, cofając prawo Conwaya.

Jeśli uważasz, że ograniczone konteksty są dostosowane do usług, może to być łatwiejsze: czy możesz mieć możliwość niezależnego wdrożenia tych dwóch funkcji? Tak sugeruje dwa ograniczone konteksty; ale jeśli muszą być zsynchronizowane, może to tylko jeden.

VoiceOfUnreason
źródło
Istnieje logika sprawdzania poprawności i domyślności, ale ma ona zastosowanie tylko podczas tworzenia / aktualizacji surowych danych wejściowych dla rachunku inwestycyjnego. Następnie używamy znacznie bogatszego modelu rachunku inwestycyjnego, gdy używamy go jako danych wejściowych do silnika obliczeniowego. Więc może mamy tutaj dwa ograniczone konteksty? Każdy z innym modelem rachunku inwestycyjnego ”
wired_in
Właśnie wróciłem do tego po kilku latach, a twój komentarz z jakiegoś powodu rozbrzmiewa teraz bardziej niż wcześniej. Jest tu wiele dobrych rzeczy, ale czy mógłbyś mi wyjaśnić jedną rzecz? Powiedziałeś: „W końcu celem modelu domeny jest zapewnienie, że wszystkie aktualizacje danych utrzymają obecny niezmiennik biznesowy”. Dotyczy to części naszej aplikacji, która zapisuje / aktualizuje informacje. Druga część to tylko silnik obliczeniowy. Bierze reprezentację danych jako dane wejściowe i wyrzuca wyniki. Czy to nie jest część modelu domeny? Ponieważ nie wpływa to na przechowywane dane?
wired_in
2

W swojej domenie nie powinieneś wiedzieć, że baza danych w ogóle istnieje.

Twoja domena dotyczy reguł biznesowych. Rzeczy, które muszą przetrwać, gdy firma, która stworzyła bazę danych, przestaje działać. To znaczy, jeśli chcesz, aby Twoja firma przetrwała. To naprawdę miłe, gdy te zasady nie dbają o to, że zmieniłeś sposób przechowywania danych.

Dane dotyczące bazy danych istnieją i należy się nimi zająć. Powinni mieszkać gdzie indziej. Umieść je przez granicę. Ostrożnie kontroluj, w jaki sposób komunikujesz się przez tę granicę, w przeciwnym razie nie jest to granica.

Wujek Bob ma to do powiedzenia na temat tego, w co umieścić swoje dane:

Zazwyczaj dane przekraczające granice są prostymi strukturami danych. Jeśli chcesz, możesz użyć podstawowych struktur lub prostych obiektów przenoszenia danych. Lub dane mogą być po prostu argumentami w wywołaniach funkcji. Możesz też spakować go do haszapy lub skonstruować w obiekt.

Ważne jest to, że izolowane, proste struktury danych są przekraczane przez granice. Nie chcemy oszukiwać i przekazywać wierszy jednostek ani baz danych. Nie chcemy, aby struktury danych miały jakąkolwiek zależność, która narusza zasadę zależności.

[…] Kiedy przekazujemy dane przez granicę, zawsze ma ona postać najbardziej dogodną dla wewnętrznego okręgu.

Czysta architektura

Wyjaśnia również, w jaki sposób zewnętrzne warstwy powinny być wtyczkami do wewnętrznych warstw, aby wewnętrzne warstwy nawet nie wiedziały, że istnieją zewnętrzne.

Ściągawka z czystą architekturą

Wykonaj coś takiego, a będziesz mieć dobre miejsce do zignorowania bazy danych, w której możesz martwić się o reguły sprawdzania poprawności danych wejściowych, reguły, które trzeba w jakiś sposób utrwalić, reguły uruchamiania obliczeń, reguły wysyłania tych wyników do dowolnego wyniku. W rzeczywistości łatwiej jest odczytać tego rodzaju kod.

Albo to, albo zdecydujesz, że twoja domena jest po prostu do manipulowania bazą danych. W takim przypadku Twoim językiem domeny jest SQL. Jeśli tak dobrze, ale nie oczekuj, że wdrożenie reguł biznesowych przetrwa zmianę trwałości. Będziesz musiał całkowicie je przepisać.

candied_orange
źródło
Używamy ORM (Entity Framework), więc nasza baza danych jest już wyodrębniona, ale modele danych (klasy Entity Framework) naturalnie są w przybliżeniu 1 do 1 z tabelami bazy danych. Problem polega na tym, że w niektórych częściach naszej aplikacji użytkownik zasadniczo aktualizuje model danych (ekran to tylko lista pól tekstowych, gdzie każde pole tekstowe jest polem w bazie danych (model danych).
wired_in
Nie widzę więc powodu, aby nie używać reprezentacji surowych danych (modelu danych) podczas wykonywania operacji CRUD. Mamy złożoną reprezentację domeny używaną do obliczeń, co widzę jako nasz model domeny, ale nie rozumiem, dlaczego załadowałbym ten obraz w części CRUD naszej aplikacji.
wired_in
Zdefiniuj, co rozumiesz przez „użyj reprezentacji surowych danych”. Dane są wprowadzane, dane są sprawdzane zgodnie z regułami domeny, dane są w jakiś sposób utrwalane, dane są obliczane względem, wyniki są wyprowadzane na cokolwiek. Czy coś brakuje?
candied_orange
Staram się powiedzieć, że nieprzetworzone dane, które otrzymujemy od użytkownika dla rachunku inwestycyjnego, nie odzwierciedlają tego, jak reprezentujemy to konto inwestycyjne w głównych częściach naszej aplikacji, na przykład w przypadku, gdy jest ono używane dla cieląt. Na przykład możemy mieć zapis danych logicznych zapisywany w bazie danych o nazwie IsManagedAccount. Użytkownik dostarcza nam to za pomocą przycisku opcji na ekranie edycji. Tak więc reprezentacja od bazy danych aż do ekranu wynosi od 1 do 1. Gdy budujemy nasz model domeny w dalszej części aplikacji, możemy mieć klasę ManagedAccount, a zatem nie ma właściwości boolowskiej. Te dwie struktury są bardzo różne.
wired_in
Kiedy więc użytkownik edytuje surowe dane wejściowe na ekranie edycji, dlaczego miałbym ładować obraz domeny, a następnie dodawać wiele złożoności, aby jakoś odwzorować silnie wpisaną klasę ManagedAccount z powrotem na płaską reprezentację, która jest tylko jedną klasą z IsManagedAccount własność?
wired_in
1

Zastosowanie teorii DDD:

W tej domenie są dwa powiązane konteksty:

  • Obliczenia rachunku inwestycyjnego. Model matematyczny rachunku inwestycyjnego jest jednym z elementów, być może agregatu.
  • Podstawowe finanse. Rachunek inwestycyjny klienta jest jednym z podmiotów.

Każdy ograniczony kontekst może mieć inny projekt architektoniczny.

Przykład:

Konto inwestycyjne klienta jest Podmiotem (być może Agregatem, zależnym od domeny), a utrwalanie danych odbywa się za pośrednictwem Repozytorium Podmiotu (RDB lub innego typu DB, takiego jak Baza danych OO).

Nie ma podejścia DDD do operacji CRUD. Powiązanie pola DB z danymi obiektu łamie zasady projektowania.

Derek
źródło