Projektowanie modelu domeny mikrousługi
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.
Zdefiniuj jeden zaawansowany model domeny dla każdej mikrousługi biznesowej lub kontekstu ograniczonego.
Twoim celem jest utworzenie pojedynczego współistnienia modelu domeny dla każdej mikrousługi biznesowej lub powiązanego kontekstu (BC). Należy jednak pamiętać, że mikrousługa BC lub biznesowa może czasami składać się z kilku usług fizycznych, które współużytkujące pojedynczy model domeny. Model domeny musi przechwytywać reguły, zachowanie, język biznesowy i ograniczenia pojedynczego kontekstu ograniczonego lub mikrousługi biznesowej, którą reprezentuje.
Wzorzec jednostki domeny
Jednostki reprezentują obiekty domeny i są definiowane głównie przez ich tożsamość, ciągłość i trwałość w czasie, a nie tylko przez atrybuty, które je tworzą. Jak mówi Eric Evans, "obiekt zdefiniowany głównie przez jego tożsamość jest nazywany jednostką". Jednostki są bardzo ważne w modelu domeny, ponieważ są podstawą modelu. Dlatego należy dokładnie je zidentyfikować i zaprojektować.
Tożsamość jednostki może przekraczać wiele mikrousług lub ograniczonych kontekstów.
Ta sama tożsamość (czyli ta sama wartość, chociaż być może nie ta sama Id
jednostka domeny) może być modelowana w wielu kontekstach ograniczonych lub mikrousługach. Nie oznacza to jednak, że ta sama jednostka z tymi samymi atrybutami i logiką zostanie zaimplementowana w wielu kontekstach ograniczonych. Zamiast tego jednostki w każdym powiązanym kontekście ograniczają ich atrybuty i zachowania do tych wymaganych w domenie ograniczonego kontekstu.
Na przykład jednostka kupującego może mieć większość atrybutów osoby zdefiniowanych w jednostce użytkownika w profilu lub mikrousłudze tożsamości, w tym tożsamości. Jednak jednostka kupującego w mikrousłudze zamawiania może mieć mniej atrybutów, ponieważ tylko niektóre dane nabywcy są powiązane z procesem zamówienia. Kontekst każdej mikrousługi lub kontekstu ograniczonego ma wpływ na model domeny.
Jednostki domeny muszą implementować zachowanie oprócz implementowania atrybutów danych.
Jednostka domeny w DDD musi zaimplementować logikę domeny lub zachowanie związane z danymi jednostki (obiekt dostępny w pamięci). Na przykład w ramach klasy jednostki zamówienia musisz mieć logikę biznesową i operacje zaimplementowane jako metody zadań, takich jak dodawanie elementu zamówienia, walidacja danych i łączne obliczenie. Metody jednostki zajmują się niezmiennymi i regułami jednostki, zamiast mieć te reguły rozłożone na warstwę aplikacji.
Rysunek 7–8 przedstawia jednostkę domeny, która implementuje nie tylko atrybuty danych, ale operacje lub metody z powiązaną logiką domeny.
Rysunek 7–8. Przykład projektu jednostki domeny implementowania danych i zachowania
Jednostka modelu domeny implementuje zachowania za pomocą metod, czyli nie jest to model "anemiczny". Oczywiście czasami można mieć jednostki, które nie implementują żadnej logiki w ramach klasy jednostki. Może się to zdarzyć w jednostkach podrzędnych w ramach agregacji, jeśli jednostka podrzędna nie ma żadnej specjalnej logiki, ponieważ większość logiki jest zdefiniowana w zagregowanym katalogu głównym. Jeśli masz złożoną mikrousługę, która ma logikę zaimplementowaną w klasach usług zamiast w jednostkach domeny, możesz wpaść do modelu domeny anemicznej, wyjaśniono w poniższej sekcji.
Rozbudowany model domeny a model domeny anemicznej
W swoim poście AnemicDomainModel Martin Fowler opisuje w ten sposób model domeny anemicznej:
Podstawowym objawem anemicznego modelu domeny jest to, że na początku rumieniec wygląda jak prawdziwa rzecz. Istnieją obiekty, wiele nazwanych za pomocą nounów w przestrzeni domeny, a te obiekty są połączone z bogatymi relacjami i strukturą, które mają prawdziwe modele domen. Catch przychodzi, gdy patrzysz na zachowanie, i zdajesz sobie sprawę, że prawie nie ma żadnego zachowania na tych obiektach, co czyni je niewiele więcej niż worki getters i setters.
Oczywiście w przypadku korzystania z modelu domeny anemicznej te modele danych będą używane z zestawu obiektów usługi (tradycyjnie nazywanej warstwą biznesową), które przechwytują całą domenę lub logikę biznesową. Warstwa biznesowa znajduje się na szczycie modelu danych i używa modelu danych tak samo jak dane.
Model domeny anemicznej to tylko projekt stylu proceduralnego. Obiekty jednostek anemicznych nie są obiektami rzeczywistymi, ponieważ nie mają zachowania (metod). Przechowują tylko właściwości danych, a tym samym nie są projektami obiektowymi. Umieszczając wszystkie zachowania w obiektach usługi (warstwie biznesowej), zasadniczo kończy się spaghetti kodu lub skryptów transakcji, a w związku z tym tracisz zalety zapewniane przez model domeny.
Niezależnie od tego, czy mikrousługę lub kontekst ograniczony jest bardzo prosty (usługa CRUD), model domeny anemicznej w postaci obiektów jednostki z tylko właściwościami danych może być wystarczająco dobry i może nie być warte zaimplementowania bardziej złożonych wzorców DDD. W takim przypadku będzie to po prostu model trwałości, ponieważ celowo utworzono jednostkę z danymi tylko do celów CRUD.
Dlatego architektury mikrousług doskonale nadają się do podejścia z wieloma architekturami w zależności od każdego kontekstu ograniczonego. Na przykład w eShopOnContainers mikrousługi zamawiania implementuje wzorce DDD, ale mikrousługa wykazu, która jest prostą usługą CRUD, nie.
Niektórzy mówią, że model domeny anemicznej jest antywzór. To naprawdę zależy od tego, co implementujesz. Jeśli utworzona mikrousługa jest wystarczająco prosta (na przykład usługa CRUD), zgodnie z modelem domeny anemicznej nie jest to wzorzec antywzór. Jeśli jednak musisz rozwiązać problem ze złożonością domeny mikrousługi, która ma wiele stale zmieniających się reguł biznesowych, model domeny anemicznej może być antywzorem dla tej mikrousługi lub kontekstu ograniczonego. W takim przypadku zaprojektowanie go jako rozbudowanego modelu zawierającego jednostki zawierające dane oraz zachowanie, a także zaimplementowanie dodatkowych wzorców DDD (agregacji, obiektów wartości itp.) może mieć ogromne korzyści dla długoterminowego sukcesu takiej mikrousługi.
Dodatkowe zasoby
DevIQ. Jednostka domeny
https://deviq.com/entity/Martin Fowler. Model domeny
https://martinfowler.com/eaaCatalog/domainModel.htmlMartin Fowler. Model domeny anemicznej
https://martinfowler.com/bliki/AnemicDomainModel.html
Wzorzec obiektu wartości
Jak zauważył Eric Evans, "Wiele obiektów nie ma tożsamości koncepcyjnej. Te obiekty opisują pewne cechy rzeczy."
Jednostka wymaga tożsamości, ale istnieje wiele obiektów w systemie, które nie są takie jak wzorzec obiektu wartości. Obiekt wartości jest obiektem bez tożsamości koncepcyjnej opisującego aspekt domeny. Są to obiekty tworzone w celu reprezentowania elementów projektu, które dotyczą tylko tymczasowo. Dbasz o to, czym są, a nie o to, kim są. Przykłady obejmują liczby i ciągi, ale mogą być również pojęciami wyższego poziomu, takimi jak grupy atrybutów.
Coś, co jest jednostką w mikrousłudze, może nie być jednostką w innej mikrousłudze, ponieważ w drugim przypadku kontekst ograniczony może mieć inne znaczenie. Na przykład adres w aplikacji do handlu elektronicznego może w ogóle nie mieć tożsamości, ponieważ może reprezentować tylko grupę atrybutów profilu klienta dla osoby lub firmy. W takim przypadku adres powinien być klasyfikowany jako obiekt wartości. Jednak w aplikacji dla firmy zajmującej się energią elektryczną adres klienta może być ważny dla domeny biznesowej. W związku z tym adres musi mieć tożsamość, aby system rozliczeniowy mógł być bezpośrednio połączony z adresem. W takim przypadku adres powinien być klasyfikowany jako jednostka domeny.
Osoba o imieniu i nazwisku jest zwykle jednostką, ponieważ osoba ma tożsamość, nawet jeśli imię i nazwisko zbiegają się z innym zestawem wartości, na przykład jeśli te nazwiska odnoszą się również do innej osoby.
Obiekty wartości są trudne do zarządzania w relacyjnych bazach danych i maszynach ORM, takich jak Entity Framework (EF), podczas gdy w bazach danych zorientowanych na dokumenty łatwiej je implementować i używać.
Program EF Core 2.0 i nowsze wersje zawierają funkcję Jednostki należące, która ułatwia obsługę obiektów wartości, jak zobaczymy szczegółowo później.
Dodatkowe zasoby
Martin Fowler. Wzorzec obiektu wartości
https://martinfowler.com/bliki/ValueObject.htmlObiekt value
https://deviq.com/value-object/Obiekty wartości w tworzeniu opartym na testach
https://leanpub.com/tdd-ebook/read#leanpub-auto-value-objectsEric Evans. Projektowanie oparte na domenie: walka ze złożonością w samym sercu oprogramowania. (Książka; zawiera dyskusję na temat obiektów wartości)
https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/
Wzorzec agregacji
Model domeny zawiera klastry różnych jednostek danych i procesów, które mogą kontrolować znaczący obszar funkcjonalności, takich jak realizacja zamówienia lub spis. Bardziej szczegółową jednostką DDD jest agregacja, która opisuje klaster lub grupę jednostek i zachowań, które mogą być traktowane jako jednostka spójna.
Zwykle definiujesz agregację na podstawie potrzebnych transakcji. Klasycznym przykładem jest kolejność zawierająca również listę elementów zamówienia. Element zamówienia będzie zwykle jednostką. Jednak będzie to jednostka podrzędna w ramach agregacji zamówienia, która będzie również zawierać jednostkę zamówienia jako jednostkę główną, zazwyczaj nazywaną zagregowanym katalogiem głównym.
Identyfikowanie agregacji może być trudne. Agregacja to grupa obiektów, które muszą być spójne razem, ale nie można po prostu wybrać grupy obiektów i oznaczyć je zagregowaną. Musisz zacząć od koncepcji domeny i zastanowić się nad jednostkami, które są używane w najbardziej typowych transakcjach związanych z tym pojęciem. Te jednostki, które muszą być spójne transakcyjnie, są czymś, co stanowi agregację. Myślenie o operacjach transakcji jest prawdopodobnie najlepszym sposobem identyfikowania agregacji.
Wzorzec agregacji głównej lub głównej jednostki
Agregacja składa się z co najmniej jednej jednostki: zagregowanego katalogu głównego, nazywanego również jednostką główną lub jednostką podstawową. Ponadto może mieć wiele podrzędnych jednostek i obiektów wartości, a wszystkie jednostki i obiekty współpracują ze sobą w celu zaimplementowania wymaganego zachowania i transakcji.
Celem zagregowanego katalogu głównego jest zapewnienie spójności agregacji; powinien być jedynym punktem wejścia dla aktualizacji agregacji za pomocą metod lub operacji w zagregowanej klasie głównej. Należy wprowadzić zmiany w jednostkach w ramach agregacji tylko za pośrednictwem zagregowanego katalogu głównego. Jest to strażnik spójności agregacji, biorąc pod uwagę wszystkie niezmienne i reguły spójności, które mogą być konieczne do zachowania zgodności w agregacji. Jeśli niezależnie zmienisz obiekt jednostki podrzędnej lub wartości, zagregowany katalog główny nie może upewnić się, że agregacja jest w prawidłowym stanie. To byłoby jak stół z luźną nogą. Utrzymywanie spójności jest głównym celem zagregowanego katalogu głównego.
Na rysunku 7–9 można zobaczyć przykładowe agregacje, takie jak agregacja nabywcy, która zawiera jedną jednostkę (zagregowany nabywca główny). Agregacja kolejności zawiera wiele jednostek i obiekt wartości.
Rysunek 7–9. Przykład agregacji z wieloma lub pojedynczymi jednostkami
Model domeny DDD składa się z agregacji, agregacja może mieć tylko jedną jednostkę lub więcej i może również zawierać obiekty wartości. Należy pamiętać, że agregacja Nabywca może mieć dodatkowe jednostki podrzędne, w zależności od domeny, podobnie jak w przypadku zamawiania mikrousługi w aplikacji referencyjnej eShopOnContainers. Rysunek 7–9 przedstawia tylko przypadek, w którym nabywca ma jedną jednostkę, jako przykład agregacji zawierającej tylko zagregowany element główny.
Aby zachować separację agregacji i zachować jasne granice między nimi, dobrym rozwiązaniem w modelu domeny DDD jest uniemożliwienie bezpośredniej nawigacji między agregacjami i tylko posiadaniem pola klucza obcego (FK), jak zaimplementowano w modelu domeny ordering mikrousługi w eShopOnContainers. Jednostka Order ma tylko pole klucza obcego dla nabywcy, ale nie właściwość nawigacji platformy EF Core, jak pokazano w poniższym kodzie:
public class Order : Entity, IAggregateRoot
{
private DateTime _orderDate;
public Address Address { get; private set; }
private int? _buyerId; // FK pointing to a different aggregate root
public OrderStatus OrderStatus { get; private set; }
private readonly List<OrderItem> _orderItems;
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
// ... Additional code
}
Identyfikowanie i praca z agregacjami wymaga badań i doświadczenia. Aby uzyskać więcej informacji, zobacz następującą listę Dodatkowych zasobów.
Dodatkowe zasoby
Vaughn Vernon. Skuteczny projekt agregacji — część I: modelowanie pojedynczej agregacji (z https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_1.pdfVaughn Vernon. Skuteczny projekt agregacji — część II: łączenie agregacji (z https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdfVaughn Vernon. Skuteczny projekt agregacji — część III: uzyskiwanie wglądu w odnajdywanie (z https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_3.pdfSiergiej Grybniak. Wzorce projektowe taktyczne DDD
https://www.codeproject.com/Articles/1164363/Domain-Driven-Design-Tactical-Design-Patterns-PartChris Richardson. Tworzenie transakcyjnych mikrousług przy użyciu agregacji
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardsonDevIQ. Wzorzec agregacji
https://deviq.com/aggregate-pattern/