Udostępnij za pośrednictwem


Projektowanie modelu domeny mikrousługi

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.

Miniatura okładki eBooka „Architektura mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET”.

Zdefiniuj jeden zaawansowany model domeny dla każdej mikrousługi biznesowej lub kontekstu ograniczonego.

Twoim celem jest utworzenie pojedynczego spójnego modelu domeny dla każdej mikrousługi biznesowej lub Ograniczonego 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ą 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.

Diagram przedstawiający wzorzec jednostki domeny.

Rysunek 7–8. Przykład projektu jednostki obszaru implementującego dane oraz zachowanie

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 pierwszy rzut oka wygląda jak coś prawdziwego. 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. Rzecz w tym, że gdy patrzysz na zachowanie, zdajesz sobie sprawę, że prawie nie ma żadnego zachowania tych obiektów, co czyni je niewiele więcej niż worki akcesorów i modyfikatorów.

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 traktuje model danych wyłącznie jako dane.

Anemiczny model domeny to po prostu projekt w stylu proceduralnym. 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ługowych (warstwie biznesowej), zasadniczo prowadzi to do kodu spaghetti lub w formie skryptów transakcyjnych, a w związku z tym tracisz korzyści, jakie zapewnia model domeny.

Niezależnie od tego, czy Twoja mikrousługa lub Kontekst Ograniczony jest bardzo prosty (na przykład usługa CRUD), model domeny anemicznej w postaci obiektów jednostki, które zawierają tylko właściwości danych, może być wystarczający i może nie być uzasadnione wdrażanie 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 modułowego opartego na każdym ograniczonym kontekście. Na przykład, w eShopOnContainers mikrousługa zamówień implementuje wzorce DDD, ale mikrousługa katalogu, która jest prostą usługą CRUD, tego nie robi.

Niektórzy twierdzą, że anemiczny model domeny jest antywzorcem. To naprawdę zależy od tego, co implementujesz. Jeśli tworzona mikrousługa jest wystarczająco prosta (na przykład usługa CRUD), nie jest to antywzór zgodnie z modelem domeny anemicznej. 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

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, które tworzysz, aby reprezentować elementy projektu, które dotyczą Cię 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

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 zamówienie zawierające również listę pozycji zamówienia. Element zamówienia będzie zwykle jednostką. Jednak będzie to jednostka podrzędna w agregacie zamówienia, która będzie również zawierać jednostkę zamówienia jako jednostkę korzenia, zazwyczaj nazywaną korzeniem agregatu.

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

Agregat składa się z co najmniej jednej jednostki: korzenia agregatu, nazywanego również jednostką główną lub jednostką nadrzędną. 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 korzenia agregatu jest zapewnienie spójności agregatu; powinien być jedynym punktem wejścia dla aktualizacji agregatu poprzez metody lub operacje w klasie korzenia agregatu. Należy wprowadzać zmiany w jednostkach w ramach agregatu tylko poprzez korzeń agregatu. 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 jednostkę podrzędną lub obiekt wartościowy, korzeń agregatu 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 korzenia agregatu.

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.

Diagram przedstawiający porównanie agregacji nabywcy i agregacji zamówień.

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 Nabywcy może mieć dodatkowe elementy podrzędne, w zależności od domeny, podobnie jak w przypadku mikrousługi zamawiania 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ę agregatów i jasne granice między nimi, dobrym rozwiązaniem w modelu domeny DDD jest uniemożliwienie bezpośredniej nawigacji między agregatami i skorzystanie jedynie z pola klucza obcego (FK), jak zaimplementowano w modelu domeny mikrousługi Ordering eShopOnContainers. Encja Order ma tylko pole klucza obcego dla nabywcy, ale nie ma właściwości nawigacji EF Core, co 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