Pracuję nad projektem, w którym staramy się zastosować zarówno projektowanie oparte na domenie, jak i REST do architektury zorientowanej na usługi. Nie martwimy się o 100% zgodność z REST; prawdopodobnie lepiej byłoby powiedzieć, że próbujemy budować API HTTP zorientowane na zasoby (~ Poziom 2 modelu dojrzałości REST Richardsona). Niemniej jednak staramy się trzymać z daleka od używania żądań HTTP w stylu RPC, tzn. Próbujemy implementować nasze czasowniki HTTP zgodnie z RFC2616 zamiast na przykład POST
robić to IsPostalAddressValid(...)
, co robimy .
Wydaje się jednak, że nacisk na to leży kosztem naszej próby zastosowania projektowania opartego na domenie. Z tylko GET
, POST
, PUT
, DELETE
i kilka innych rzadko stosowane metody, mamy tendencję do budowania usług cruddy i usługi cruddy mają tendencję do anemiczny model domeny.
POST
: Odbierz dane, zweryfikuj je, zrzuć do danych. GET
: Pobierz dane, zwróć je. Nie ma tam prawdziwej logiki biznesowej. Używamy również komunikatów (zdarzeń) między usługami i wydaje mi się, że większość logiki biznesowej kończy się wokół tego.
Czy REST i DDD są na pewnym poziomie napięcia? (A może coś tu źle rozumiem? Czy robimy coś innego źle?) Czy można zbudować silny model domeny w architekturze zorientowanej na usługi, unikając wywołań HTTP w stylu RPC?
IsPostalAddressValid(...)
byłoby zgodne z „Zapewnieniem bloku danych, takiego jak wynik przesłania formularza, do procesu przetwarzania danych”?Odpowiedzi:
Pierwsza zasada systemów rozproszonych Martina Fowlera: „Nie rozpowszechniaj swoich obiektów!” Interfejsy zdalne powinny być gruboziarniste, a interfejsy wewnętrzne drobnoziarniste. Często bogaty model domeny ma zastosowanie tylko w ograniczonym kontekście .
Interfejs API REST oddziela dwa różne konteksty, z których oba mają własne modele wewnętrzne. Konteksty komunikują się przez interfejs gruboziarnisty (REST API) przy użyciu obiektów „anemicznych” (DTO).
W twoim przypadku brzmi to tak, jakbyś próbował rozszerzyć kontekst poza granicę, którą jest interfejs API REST. Może to prowadzić do drobnoziarnistego interfejsu zdalnego lub modelu anemicznego. W zależności od projektu może to stanowić problem.
źródło
POST został celowo zaprojektowany, aby był „celowo niejasny”; wynik testu POST jest specyficzny dla implementacji. Co powstrzymuje cię przed robieniem tego, co robią Twitter i inni projektanci API, i definiowaniem każdej metody POST w części interfejsu API innej niż CRUD zgodnie z własnymi konkretnymi wymaganiami? POST jest czasownikiem catchall. Użyj go, gdy żaden z innych czasowników nie pasuje do operacji, którą chcesz wykonać.
Innymi słowy, twoje pytanie może być równie postawione, ponieważ „Czy„ inteligentne ”obiekty zachęcają do projektowania w stylu RPC?” Nawet Martin Fowler (który ukuł termin „Anemic Domain Model”) przyznaje, że nagie DTO mają pewne zalety:
Jeśli chodzi o model dojrzałości Richardsona , możesz dostać się na poziom 3, nie martwiąc się o „Anemiczne modele domen”. Pamiętaj, że nigdy nie zamierzasz przenosić zachowania do przeglądarki (chyba że planujesz wstrzyknąć JavaScript w swoich modelach).
REST dotyczy głównie niezależności maszyn; zaimplementuj model REST w takim stopniu, w jakim chcesz, aby punkty końcowe reprezentowały zasoby, oraz aby użytkownicy interfejsu API mogli łatwo uzyskać dostęp do tych zasobów i utrzymywać je w standardowy sposób. Jeśli to wydaje się anemiczne, niech tak będzie.
Zobacz także
Potrzebuję więcej czasowników
źródło
Interfejs API REST jest tylko jednym rodzajem warstwy prezentacji. Nie ma to nic wspólnego z modelem domeny.
Pytanie, które zadałeś, pochodzi z zamieszania, że musisz jakoś dostosować się do siebie. Ty nie.
Mapujesz model domeny na interfejs API REST w taki sam sposób, jak mapujesz model domeny na RDBMS za pośrednictwem ORM - musi istnieć ta warstwa mapowania.
Domena ← ORM →
Domena RDBMS ← Mapowanie REST → API REST
źródło
IMHO Nie sądzę, aby zachęcały do modelowania domen anemicznych (ADM), ale wymagają poświęcenia czasu i przemyślenia.
Po pierwsze, myślę, że główną cechą ADM jest to, że nie mają w nich żadnego lub wcale się nie zachowują. Nie oznacza to, że system nie zachowuje się, tylko że zwykle znajduje się w jakiejś klasie usług (patrz http://vimeo.com/43598193 ).
I oczywiście, jeśli takie zachowanie nie istnieje w ADM, to co? Odpowiedzią są oczywiście dane. I w jaki sposób mapuje się na API REST? Prawdopodobnie dane odwzorowują zawartość zasobu, a zachowanie odwzorowuje czasowniki HTTP.
Masz więc wszystko, czego potrzebujesz, aby zbudować bogaty model domeny, po prostu musisz zobaczyć, jak czasowniki HTTP odwzorowują operacje domeny na danych, a następnie umieścić te operacje w tych samych klasach, które zawierają dane.
Myślę, że ludzie mają problemy z tym, że mają trudności z zobaczeniem, jak czasowniki HTTP odwzorowują ich zachowanie w domenie, gdy zachowanie wykracza poza zwykłą CRUD, tj. Gdy występują skutki uboczne w innych częściach domeny poza zasób modyfikowany przez żądanie HTTP. Jednym ze sposobów rozwiązania tego problemu są zdarzenia domeny ( http://www.udidahan.com/2009/06/14/domain-events-salvation/ ).
źródło
Ten artykuł jest dość związany z tematem i wierzę, że odpowiada na twoje pytanie.
Podstawowa koncepcja, która moim zdaniem odpowiada bardzo dobrze na twoje pytanie, została streszczona w następującym akapicie wspomnianego artykułu:
„Bardzo ważne jest rozróżnienie między zasobami w interfejsie API REST i jednostkami domeny w projekcie opartym na domenie. Projekt oparty na domenie ma zastosowanie po stronie implementacji (w tym implementacji API), podczas gdy zasoby w interfejsie API REST sterują projektem interfejsu API i kontraktem. Zasób API wybór nie powinien zależeć od podstawowych szczegółów implementacji domeny. ”
źródło
Kilka rozsądnie udanych wdrożeń, które widziałem / zbudowałem, odpowiada na pytanie, w jaki sposób łączą one czasownik + metaforę rzeczownika przy użyciu gruboziarnistych metod „przyjaznych dla biznesu”, które działają na podmioty.
Więc zamiast (skazanej na atak)
getName()
metody / usługi ujawnijgetPerson()
, przekazując rzeczy takie jak typ identyfikatora / identyfikator, zwracając całyPerson
byt.Ponieważ zachowania encji Osoba w takim kontekście nie mogą być odpowiednio przekazane (ani też nie powinny być w kontekście skoncentrowanym na danych, jak to), całkowicie uzasadnione jest zdefiniowanie modelu danych (w stosunku do obiektu) dla par zapytania / odpowiedzi Usługi.
Same usługi i zdefiniowane czasowniki dodają pewne zachowania, kontrole, a nawet reguły przejścia do stanu dla jednostek. Na przykład istniałaby logika specyficzna dla domeny co do tego, co dzieje się w
transferPerson()
wywołaniu usługi, ale sam interfejs definiowałby tylko dane wejściowe / wyjściowe / dane bez definiowania ICH zachowań wewnętrznych.Nie zgadzam się z autorami, którzy powiedzieliby na przykład, że implementacja czasownika przeniesienia należy do klasy Person lub jest powiązana z usługą Person-centric. Rzeczywiście, metoda transferu dotyczący
Person
oraz opcje ich (w tym prostym przykładzie) byłoby lepiej zdefiniowane przezCarrier
, w którymPerson
może mieć żadnej wiedzy nawet, jakie są dostępne metody transferu lub jak przenoszenie nawet ma miejsce (kto wie Pracy Jak silników odrzutowych tak czy inaczej).Czy to powoduje, że
Person
istota jest anemiczna? Nie wydaje mi sięMoże / powinna istnieć logika w odniesieniu do rzeczy specyficznych dla Osoby, które są wewnętrzne dla Osoby, takich jak ich stan zdrowia, które nie powinny być definiowane przez klasę zewnętrzną.
Jednak w zależności od przypadków użycia jest całkowicie do przyjęcia, że klasa jednostek nie ma ważnych / istotnych zachowań w niektórych systemach, takich jak usługa przydzielania miejsc w systemie transportowym. Taki system może równie dobrze implementować usługi oparte na REST, które zajmują się instancjami Person i powiązanymi identyfikatorami, ale nigdy nie definiują / implementują ich wewnętrznych zachowań.
źródło
Czy masz problem z tym, że próbujesz wcisnąć swój model w podstawowy zestaw czasowników, używając POST tak często, jak to możliwe?
To nie jest konieczne - wiem, że dla większości ludzi REST oznacza POST, GET, PUT i DELETE, ale http rfc mówi:
Systemy takie jak SMTP używają tego samego stylu metod opartych na czasownikach, ale z zupełnie innym zestawem.
Więc nie ma powodu, dla którego musisz ich używać, możesz użyć dowolnego zestawu czasowników, który ci się podoba (chociaż naprawdę przekonasz się, że możesz zrobić wszystko, czego potrzebujesz w podstawowej 4 z odrobiną przemyślenia). Tym, co odróżnia REST od innych mechanizmów, jest jego bezstanowy i spójny sposób implementacji tych czasowników. Nie powinieneś próbować wdrażać systemu przekazywania wiadomości między warstwami, ponieważ zasadniczo nie wykonujesz REST, zamiast tego robisz mechanizm przekazywania wiadomości, RPC lub kolejki komunikatów, co niewątpliwie straci ci korzyści z REST (tj. jego prostota sprawia, że działa naprawdę dobrze przez połączenie HTTP).
Jeśli potrzebujesz w pełni funkcjonalnego, złożonego protokołu przesyłania wiadomości, zbuduj go (jeśli możesz to zrobić przez Internet, istnieje powód, dla którego REST jest tak popularny), ale w przeciwnym razie staraj się trzymać projektu architektonicznego REST.
źródło