Udostępnij za pośrednictwem


Projektowanie mikrousługi zorientowanej na DDD

Napiwek

Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

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 Powiązane konteksty (każdy kontekst ograniczony koreluje z mikrousługą) 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, agregacje i reguły agregacji katalogu głównego (lub jednostki głównej) 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 kończą się mikrousługami, chociaż w niektórych przypadkach mikrousługi bc lub biznesowe mogą składać się z kilku usług fizycznych. DDD dotyczy granic i tak są 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, chcesz uniknąć czatty 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ą współpracować ze sobą wiele, prawdopodobnie powinny one 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 są artefaktem logicznym i nie są powiązane z wdrażaniem 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. Jest to powód, dla którego jest model ViewModel. Model ViewModel to model danych przeznaczony wyłącznie dla potrzeb warstwy prezentacji. Jednostki domeny nie należą bezpośrednio do modelu ViewModel. Zamiast tego należy przetłumaczyć między modelami ViewModel i jednostkami domeny i odwrotnie.

Podczas rozwiązywania problemów ze złożonością należy mieć model domeny kontrolowany przez zagregowane korzenie, które zapewniają, że wszystkie niezmienne i reguły związane z grupą jednostek (agregacja) są wykonywane za pośrednictwem pojedynczego punktu wejścia lub bramy, głównego agregacji.

Rysunek 7–5 pokazuje, jak projekt warstwowy jest implementowany w aplikacji eShopOnContainers.

Diagram showing the layers in a domain-driven design microservice.

Rysunek 7–5. Warstwy DDD w mikrousłudze porządkowania 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.

Screenshot of Ordering.Domain dependencies.

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 Eric Evans Domain Driven Design mówi następujące informacje o warstwie modelu domeny i warstwie 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ą delegowane do infrastruktury. Ta warstwa jest sercem oprogramowania biznesowego.

Warstwa modelu domeny to miejsce, w którym jest wyrażona firma. 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ą Trwałości ignorancji dla modelu domeny, nie należy ignorować obaw o trwałość. 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 cienka. Nie zawiera reguł biznesowych ani wiedzy, ale koordynuje tylko zadania i delegaty do współpracy obiektów domeny 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 internetowego interfejsu API platformy ASP.NET Core. Projekt implementuje interakcję mikrousługi, dostęp do sieci zdalnej i zewnętrzne internetowe 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 on wykonywanie reguł biznesowych do samych klas modelu domeny (agregacja katalogów głównych i jednostek domeny), które ostatecznie zaktualizują dane w tych jednostkach domeny.

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ą internetowego interfejsu 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 warstw modelu domeny powinna zawierać tylko kod domeny, tylko klasy jednostek POCO implementowane serce oprogramowania i całkowicie oddzielone 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.

Diagram showing dependencies that exist between DDD service layers.

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