Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Ta treść jest fragmentem eBooka "Architektura mikrousług .NET dla konteneryzowanych aplikacji .NET", dostępnego na .NET Docs lub jako bezpłatny plik PDF do pobrania i czytania w trybie offline.
Projektowanie oparte na domenie (DDD) opowiada się za modelowaniem w oparciu o rzeczywistość biznesową, która jest odpowiednia dla Twoich przypadków użycia. W kontekście tworzenia aplikacji DDD mówi o problemach jako domenach. Opisuje on niezależne obszary problemów jako Ograniczone konteksty (każdy Ograniczony kontekst odpowiada mikrousłudze) i podkreśla wspólny język, aby mówić o tych problemach. Sugeruje również wiele pojęć technicznych i wzorców, takich jak jednostki domeny z bogatymi modelami (bez modelu domeny anemicznej), obiekty wartości, agregaty i reguły katalogu głównego dla agregatów (lub jednostki korzeniowej) do obsługi implementacji wewnętrznej. W tej sekcji przedstawiono projekt i implementację tych wzorców wewnętrznych.
Czasami te reguły techniczne i wzorce DDD są postrzegane jako przeszkody, które mają stromą krzywą uczenia na potrzeby implementowania podejść DDD. Jednak ważną częścią nie są same wzorce, ale organizowanie kodu, więc jest dostosowane do problemów biznesowych i używanie tych samych terminów biznesowych (wszechobecny język). Ponadto podejścia DDDD powinny być stosowane tylko w przypadku implementowania złożonych mikrousług ze znaczącymi regułami biznesowymi. Prostsze obowiązki, takie jak usługa CRUD, można zarządzać za pomocą prostszych metod.
Gdzie rysować granice jest kluczowym zadaniem podczas projektowania i definiowania mikrousługi. Wzorce DDD pomagają zrozumieć złożoność domeny. W przypadku modelu domeny dla każdego kontekstu ograniczonego należy zidentyfikować i zdefiniować jednostki, obiekty wartości i agregacje, które modelują domenę. Tworzysz i uściślisz model domeny, który jest zawarty w granicach definiujących kontekst. I to jest jawne w postaci mikrousługi. Składniki w tych granicach stają się twoimi mikrousługami, chociaż w niektórych przypadkach mikrousługi BC (Bounded Context) lub biznesowe mogą składać się z kilku usług fizycznych. DDD dotyczy granic, podobnie jak mikrousługi.
Zachowaj granice kontekstu mikrousługi stosunkowo małe
Określanie, gdzie należy umieścić granice między powiązanymi kontekstami, równoważy dwa konkurencyjne cele. Najpierw należy najpierw utworzyć najmniejsze możliwe mikrousługi, chociaż nie powinny być głównym sterownikiem; należy utworzyć granicę wokół rzeczy, które wymagają spójności. Po drugie, należy unikać zbyt rozmownej komunikacji między mikrousługami. Te cele mogą być ze sobą sprzeczne. Należy je zrównoważyć, rozkładając system na dowolną liczbę małych mikrousług, dopóki nie zobaczysz, że granice komunikacji szybko rosną wraz z każdą dodatkową próbą oddzielenia nowego kontekstu ograniczonego. Spójność jest kluczowa w ramach jednego ograniczonego kontekstu.
Jest on podobny do nieodpowiedniego zapachu kodu intymności podczas implementowania klas. Jeśli dwie mikrousługi muszą wiele ze sobą współpracować, prawdopodobnie powinny być tą samą mikrousługą.
Innym sposobem na przyjrzenie się temu aspektowi jest autonomia. Jeśli mikrousługa musi polegać na innej usłudze, aby bezpośrednio obsługiwać żądanie, nie jest to naprawdę autonomiczne.
Warstwy w mikrousługach DDD
Większość aplikacji dla przedsiębiorstw ze znaczną złożonością biznesową i techniczną jest definiowana przez wiele warstw. Warstwy stanowią logiczny artefakt i nie są związane z implementacją usługi. Istnieją one, aby ułatwić deweloperom zarządzanie złożonością w kodzie. Różne warstwy (takie jak warstwa modelu domeny i warstwa prezentacji itp.) mogą mieć różne typy, które nakazują tłumaczenia między tymi typami.
Na przykład jednostka może zostać załadowana z bazy danych. Następnie część tych informacji lub agregacja informacji, w tym dodatkowe dane z innych jednostek, może być wysyłana do interfejsu użytkownika klienta za pośrednictwem internetowego interfejsu API REST. Chodzi o to, że jednostka domeny jest zawarta w warstwie modelu domeny i nie powinna być propagowana do innych obszarów, do których nie należy, na przykład do warstwy prezentacji.
Ponadto należy mieć zawsze prawidłowe jednostki (zobacz sekcję Projektowanie walidacji w warstwie modelu domeny ) kontrolowane przez zagregowane jednostki główne (jednostki główne). W związku z tym jednostki nie powinny być powiązane z widokami klientów, ponieważ na poziomie interfejsu użytkownika niektóre dane mogą nadal nie być weryfikowane. Dlatego właśnie istnieje ViewModel. Model ViewModel jest modelem danych przeznaczonym wyłącznie do potrzeb warstwy wizualnej. Jednostki domeny nie należą bezpośrednio do modelu ViewModel. Zamiast tego należy dokonywać translacji między ViewModelami a encjami domenowymi i odwrotnie.
Podczas rozwiązywania problemów ze złożonością ważne jest posiadanie modelu domeny zarządzanego przez korzenie agregatu, które zapewniają, że wszystkie inwarianty i reguły związane z tą grupą jednostek (agregat) są egzekwowane przez pojedynczy punkt wejścia lub bramę, korzeń agregatu.
Rysunek 7–5 pokazuje, jak projekt warstwowy jest implementowany w aplikacji eShopOnContainers.
Rysunek 7–5. Warstwy DDD w mikrousłudze zamówień w eShopOnContainers
Trzy warstwy mikrousługi DDD, takie jak Ordering. Każda warstwa jest projektem programu VS: Warstwa aplikacji to Ordering.API, warstwa domeny to Ordering.Domain, a warstwa infrastruktury to Ordering.Infrastructure. Chcesz zaprojektować system, aby każda warstwa komunikowała się tylko z niektórymi innymi warstwami. Takie podejście może być łatwiejsze do wymuszania, jeśli warstwy są implementowane jako różne biblioteki klas, ponieważ można wyraźnie określić, jakie zależności są ustawiane między bibliotekami. Na przykład warstwa modelu domeny nie powinna być zależna od żadnej innej warstwy (klasy modelu domeny powinny być zwykłymi starymi obiektami klas lub klasami POCO). Jak pokazano na rysunku 7–6, biblioteka warstwy Ordering.Domain ma zależności tylko w bibliotekach platformy .NET lub pakietach NuGet, ale nie w żadnej innej bibliotece niestandardowej, takiej jak biblioteka danych lub biblioteka trwałości.
Rysunek 7–6. Warstwy zaimplementowane jako biblioteki umożliwiają lepszą kontrolę nad zależnościami między warstwami
Warstwa modelu domeny
Doskonała książka Erica Evansa Domain Driven Design opisuje następujące aspekty warstwy modelu domeny i warstwy aplikacji.
Warstwa modelu domeny: odpowiada za reprezentowanie pojęć biznesowych, informacji o sytuacji biznesowej i reguł biznesowych. Stan, który odzwierciedla sytuację biznesową, jest kontrolowany i używany w tym miejscu, mimo że szczegóły techniczne przechowywania są przypisane infrastrukturze. Ta warstwa jest sercem oprogramowania biznesowego.
Warstwa modelu domeny to miejsce, w którym wyrażany jest biznes. Podczas implementowania warstwy modelu domeny mikrousług na platformie .NET ta warstwa jest kodowana jako biblioteka klas z jednostkami domeny, które przechwytują dane plus zachowanie (metody z logiką).
Zgodnie z zasadami ignorancji trwałości i ignorancji infrastruktury ta warstwa musi całkowicie ignorować szczegóły trwałości danych. Te zadania trwałości powinny być wykonywane przez warstwę infrastruktury. W związku z tym ta warstwa nie powinna przyjmować bezpośrednich zależności od infrastruktury, co oznacza, że ważną regułą jest to, że klasy jednostek modelu domeny powinny być klasami POC.
Jednostki domeny nie powinny mieć żadnej bezpośredniej zależności (takiej jak wyprowadzanie z klasy bazowej) w żadnej strukturze infrastruktury dostępu do danych, takiej jak Entity Framework lub NHibernate. W idealnym przypadku jednostki domeny nie powinny pochodzić ani implementować żadnego typu zdefiniowanego w żadnej strukturze infrastruktury.
Większość nowoczesnych struktur ORM, takich jak Entity Framework Core, umożliwia takie podejście, dzięki czemu klasy modeli domeny nie są powiązane z infrastrukturą. Jednak posiadanie jednostek POCO nie zawsze jest możliwe w przypadku korzystania z niektórych baz danych i struktur NoSQL, takich jak Actors i Reliable Collections w usłudze Azure Service Fabric.
Nawet jeśli ważne jest, aby postępować zgodnie z zasadą ignorowania trwałości dla modelu domeny, nie należy ignorować kwestii związanych z trwałością. Nadal ważne jest zrozumienie modelu danych fizycznych i sposobu mapowania go na model obiektów jednostki. W przeciwnym razie można tworzyć niemożliwe projekty.
Ponadto ten aspekt nie oznacza, że można stosować model przeznaczony dla relacyjnej bazy danych i bezpośrednio przenieść go do bazy danych NoSQL lub bazy danych zorientowanej na dokumenty. W niektórych modelach jednostek model może pasować, ale zwykle nie. Nadal istnieją ograniczenia, których model jednostki musi przestrzegać, zarówno na podstawie technologii magazynowania, jak i technologii ORM.
Warstwa aplikacji
Przechodząc do warstwy aplikacji, możemy ponownie przytaczać książkę Eric Evans Domain Driven Design:
Warstwa aplikacji: Definiuje zadania, które oprogramowanie ma wykonywać, i kieruje wyraziste obiekty domeny do rozwiązywania problemów. Zadania, za które odpowiada ta warstwa, mają znaczenie dla firmy lub są niezbędne do interakcji z warstwami aplikacji innych systemów. Ta warstwa jest utrzymywana cienka. Nie zawiera reguł biznesowych ani wiedzy, a jedynie koordynuje zadania i deleguje pracę do współpracy obiektów domenowych w następnej warstwie w dół. Nie ma stanu odzwierciedlającego sytuację biznesową, ale może mieć stan, który odzwierciedla postęp zadania dla użytkownika lub programu.
Warstwa aplikacji mikrousługi na platformie .NET jest często kodowana jako projekt Web API w ASP.NET Core. Projekt implementuje interakcje mikrousług, zdalny dostęp do sieci oraz zewnętrzne webowe interfejsy API, używane z interfejsu użytkownika lub aplikacji klienckich. Obejmuje ona zapytania w przypadku korzystania z podejścia CQRS, poleceń akceptowanych przez mikrousługę, a nawet komunikacji sterowanej zdarzeniami między mikrousługami (zdarzeniami integracji). Internetowy interfejs API platformy ASP.NET Core reprezentujący warstwę aplikacji nie może zawierać reguł biznesowych ani wiedzy o domenie (zwłaszcza reguł domen dla transakcji lub aktualizacji); powinny być własnością biblioteki klas modelu domeny. Warstwa aplikacji musi koordynować tylko zadania i nie może przechowywać ani definiować żadnego stanu domeny (modelu domeny). Deleguje wykonywanie reguł biznesowych do klas modelu domenowego (korzenie agregatów i jednostki domenowe), które ostatecznie zaktualizują dane w tych jednostkach domenowych.
Zasadniczo logika aplikacji to miejsce, w którym implementujesz wszystkie przypadki użycia, które zależą od danego frontonu. Na przykład implementacja związana z usługą webowego API.
Celem jest to, że logika domeny w warstwie modelu domeny, jej niezmienności, model danych i powiązane reguły biznesowe muszą być całkowicie niezależne od warstw prezentacji i aplikacji. Przede wszystkim warstwa modelu domeny nie może bezpośrednio zależeć od żadnej struktury infrastruktury.
Warstwa infrastruktury
Warstwa infrastruktury to sposób, w jaki dane początkowo przechowywane w jednostkach domeny (w pamięci) są utrwalane w bazach danych lub innym magazynie trwałym. Przykładem jest użycie kodu Platformy Entity Framework Core w celu zaimplementowania klas wzorców repozytorium, które używają obiektu DBContext do utrwalania danych w relacyjnej bazie danych.
Zgodnie z wcześniej wymienionymi zasadami niewiedzy trwałości i ignorancji infrastruktury warstwa infrastruktury nie może "zanieczyścić" warstwy modelu domeny. Klasy jednostek modelu domeny muszą być niezależne od infrastruktury używanej do utrwalania danych (EF lub jakiejkolwiek innej struktury), nie podejmując twardych zależności od struktur. Biblioteka klas warstwy modelu domeny powinna zawierać wyłącznie kod domeny, czyli tylko klasy jednostek POCO, które stanowią rdzeń oprogramowania i są całkowicie niezależne od technologii infrastruktury.
W związku z tym warstwy lub biblioteki klas i projekty powinny ostatecznie zależeć od warstwy modelu domeny (biblioteki), a nie odwrotnie, jak pokazano na rysunku 7–7.
Rysunek 7–7. Zależności między warstwami w DDD
Zależności w usłudze DDD, warstwa aplikacji zależy od domeny i infrastruktury, a infrastruktura zależy od domeny, ale domena nie zależy od żadnej warstwy. Ten projekt warstwy powinien być niezależny dla każdej mikrousługi. Jak wspomniano wcześniej, można zaimplementować najbardziej złożone mikrousługi po wzorcach DDD, implementując prostsze mikrousługi oparte na danych (proste CRUD w jednej warstwie) w prostszy sposób.
Dodatkowe zasoby
DevIQ. Zasada ignorowania trwałości
https://deviq.com/persistence-ignorance/Oren Eini. Ignorancja infrastruktury
https://ayende.com/blog/3137/infrastructure-ignoranceAngel Lopez. Architektura warstwowa w projekcie Domain-Driven
https://ajlopez.wordpress.com/2008/09/12/layered-architecture-in-domain-driven-design/