Jak zaprojektować granice agregatów?

10

Chciałbym napisać aplikację podobną do e-commerce.

I wiesz, że w podobnych aplikacjach produkty mogą mieć różne właściwości i funkcje. Aby zasymulować taką możliwość, stworzyłem następujące podmioty modelu domeny:

Kategoria - jest to coś w rodzaju „elektronika> komputery”, czyli rodzaje produktów. Kategorie zawierają listę właściwości (Lista <Właściwość>).

Właściwość - niezależna jednostka, która zawiera nazwę, jednostki miary, typ danych. Na przykład „nazwa”, „waga”, „rozmiar ekranu”. Ta sama właściwość może mieć różne produkty.

Produkt - zawiera tylko nazwę i listę wartości odnoszących się do właściwości. Wartość to obiekt, który zawiera tylko pole wartości i identyfikator pola właściwości.

Pierwotnie zdecydowałem, aby kategoria była jak pojedynczy agregat w tym schemacie, ponieważ na przykład, gdy dodam nowy produkt, muszę znać wszystkie dane związane z bieżącą kategorią, w tym właściwości związane z bieżącą kategorią ( category.AddNewProduct (produkt) ). Ale co powinienem zrobić, gdy muszę tylko dodać nową właściwość, która nie należy do żadnej kategorii. Na przykład nie mogę zrobić tej kategorii.AddNewProperty (właściwość), ponieważ wyraźnie mówi, że dodajemy właściwość do określonej kategorii.

Ok, w następnym kroku postanowiłem rozdzielić Właściwość na osobny agregat, ale wtedy będzie to lista z prostymi jednostkami.

Oczywiście mogę stworzyć coś takiego jak PropertyAggregate, aby zachować wewnętrzną listę właściwości i reguł biznesowych, ale kiedy dodam produkt, muszę mieć wewnątrz kategorii całą listę właściwości należących do tej kategorii, aby sprawdzić niezmienniki. Ale zdaję sobie również sprawę, że trzymanie linków wewnątrz agregatu na innych agregatach jest złą praktyką.

Jakie są opcje zaprojektowania tego przypadku biznesowego?

Cephei
źródło
Czy możesz podać pełniejszy przykład kategorii, właściwości i produktu? Elektronika lub komputery byłyby kategorią, iPhone X byłby przykładem produktu, a właściwość byłaby czym dokładnie? 11-calowy wyświetlacz?
Neil
masz prawie rację. Dodałem kilka wyjaśnień
cephei
Wygląda na to, że patrzysz na projekt agregatu wyłącznie z perspektywy „kontenera danych”. Możesz także pomyśleć o przypadkach użycia Twojej aplikacji, biorąc pod uwagę aspekty transakcyjne, współpracę / równoczesny dostęp, zdarzenia, zdarzenia, przejścia między stanami itp.
guillaume31

Odpowiedzi:

7

W perspektywie DDD Category, Producti Propertysą bytami: wszystkie odpowiadają obiektom, które mają własną tożsamość.

Opcja 1: twój oryginalny projekt

Zrobiłeś Categorykorzeń jednego agregatu. Z jednej strony ma to sens, ponieważ łączna zapewnia spójność, gdy jej cele zostały zmodyfikowane, i Productmusi mieć PropertiesITS Category:

wprowadź opis zdjęcia tutaj

Ale z drugiej strony pojedynczy agregat oznacza, że ​​wszystkie jego obiekty są powiązane z katalogiem głównym, który jest ich właścicielem, a wszystkie odniesienia zewnętrzne muszą być dokonywane za pośrednictwem tego agregatu. To daje do zrozumienia ze:

  • jeden konkretny Productnależy do jednego i tylko jednego Category. Jeśli Categoryzostanie usunięty, to i jego Products.
  • określony Propertynależy do jednego i tylko jednego Category. Innymi słowy, jeśli „ekrany telewizyjne” i „monitory komputerowe” byłyby dwiema kategoriami, „ekrany telewizyjne: rozmiar” i „monitory komputerowe: rozmiar” miałyby dwie różne właściwości.

Drugi punkt nie odpowiada twojej narracji: „ Ale co powinienem zrobić, gdy muszę tylko dodać nowy Property, który nie należy do żadnej kategorii ”. I nie jest jasne, czy to samo Propertiesmożna zastosować w inny sposób Categories.

Opcja 2: Nieruchomość poza agregatem

Jeśli Propertyistnieje niezależnie od Categories, musi znajdować się poza agregatem. I to samo, jeśli chcesz dzielić Propertiesmiędzy Categories(co ma sens dla wysokości, szerokości, rozmiarów itp.). Wydaje się, że tak właśnie jest.

Konsekwencją jest powiązanie między Propertyrzeczami, które należą do agregatu: podczas gdy możesz nawigować z wnętrza agregatu do Property, nie możesz już przechodzić bezpośrednio od a Propertydo odpowiednich wartości. To ograniczenie żeglowności może być pokazane na diagramie UML:

wprowadź opis zdjęcia tutaj

Zauważ, że ten projekt nie przeszkadza ci List<Property>w Categorytworzeniu semantycznej referencji (np. Java): każda referencja na liście odnosi się do Propertyobiektu, który można współdzielić w repozytorium.

Jedynym problemem związanym z tym projektem jest to, że można go zmienić Propertylub usunąć: ponieważ jest on poza agregatem, agregat nie może zadbać o spójność jego niezmienników. Ale to nie jest problem. Jest to konsekwencja zasad DDD i złożoności świata rzeczywistego. Oto cytat Erica Evansa z jego przełomowej książki „ Domain-Driven Design: Tackling Complexity in the Heart of Software ”:

Żadna reguła, która obejmuje AGREGATY , nie będzie zawsze aktualna. Dzięki przetwarzaniu zdarzeń, przetwarzaniu wsadowemu lub innym mechanizmom aktualizacji inne zależności można rozwiązać w określonym czasie. Jednak niezmienniki zastosowane w AGREGANCIE będą egzekwowane po zakończeniu każdej transakcji.

Tak więc, jeśli zmienisz a Property, będziesz musiał upewnić się, że usługa sprawdza kategorie, które jej dotyczą, są aktualizowane w razie potrzeby.

Opcja 3: kategoria, właściwość i produkt w różnych agregatach

Zastanawiam się tylko, czy założenie, że Productnależy do singla, Categoryjest uzasadnione:

  • Często widzę sklepy internetowe proponujące jeden Productpod kilkoma Categories. Na przykład „Laptop Marka X Model Y” można znaleźć w kategorii „Laptopy” i kategoria „Komputery”, a „drukarka wielofunkcyjna Z” w kategorii „drukarka”, „skaner” i „faks”.
  • Czy nie jest możliwe, że ktoś utworzy Productpierwszy, a dopiero później przypisze go do kategorii i wypełni wartości?
  • Jeśli chcesz podzielić kategorię, czy naprawdę usunąłbyś jej Produkty, a następnie odtworzyłeś je w nowych kategoriach?

Nie uprości to agregacji, a będziesz mieć jeszcze więcej reguł, które obejmują agregaty. Ale twój system byłby o wiele bardziej odporny na przyszłość.

Christophe
źródło
Dziękuję bardzo, to bardzo przydatne wyjaśnienie. Chciałbym jednak wyjaśnić kilka kwestii. Chciałbym zacząć od drugiej opcji i kto wie, może przejdę do trzeciej. Jeśli wyjdę Propertypoza granice Categoryagregatu, czy to oznacza, że Propertystaje się ono agregatem i potrzebuje repozytorium? Jeśli to prawda, to jak przekazać wymagane List<Property>do Categoryinstancji? Przez konstruktora? Będzie dobrze? I jak mogę znaleźć listę Propertyidentyfikatorów, Categoryktóre nie zostały jeszcze utworzone?
cephei
@zetetic w skrócie: tak, potrzebujesz niezależnego repozytorium nieruchomości. Albo przekazujesz listę istniejących właściwości do fabryki kategorii, albo tworzysz puste kategorie i wypełniasz listę metodą addProperty. Pytanie w zamian: wyobraź sobie, że chcesz mieć właściwości „obowiązkowe” i „opcjonalne”, a charakterystyka obowiązkowa zależy od kategorii. Jak sobie z tym poradzisz?
Christophe
odpowiadając na twoje pytanie, pierwszą rzeczą, która przychodzi na myśl, jest to, że mogę stworzyć specjalną istotę, Featurektóra będzie należeć tylko do Product. i ten podmiot nie będzie uczestniczył w wyszukiwaniu. Co mówisz ?
cephei
@zetetic dlaczego nie! Pozostawiłbym Wartości, ponieważ są one obecnie w produkcie, i powiązałbym tę funkcję z kategorią. Kategoria ma n cech (część jej agregatu), a właściwość definiuje m cech (ale link przechodzi przez kategorię-> cecha). Następnie rozłożyłeś relację wiele do wielu na łatwiejsze do zarządzania elementy, wyjaśniając granicę agregacji. Wreszcie o iniekcji do repozytorium: nie jest to wymagane, jeśli odwołujesz się do innych agregatów według tożsamości (przeczytaj ten artykuł informit.com/articles/article.aspx?p=2020371&seqNum=4 )
Christophe
5

Moim zdaniem możesz to rozwiązać na dwa sposoby:

Kategoria to specjalny rodzaj produktu

Oznacza to, że dla każdego produktu w bazie danych zawiera klucz obcy wskazujący na ten sam produkt tabeli. Produkt jest produktem tylko wtedy, gdy nie ma produktów, których klucz obcy jest równy identyfikatorowi tego produktu. Innymi słowy, jeśli nie ma pod nim żadnych produktów, jest to produkt.

Uprościłoby to trochę. Produkty do właściwości miałyby relację jeden do wielu, a zatem również Twoje kategorie mają relację jeden do wielu, ponieważ są one również produktami. Dodanie właściwości do kategorii byłoby tak proste, jak dodanie właściwości do produktu w twoim programie. Wczytanie wszystkich właściwości oznaczałoby połączenie właściwości produktu z właściwościami powiązanego produktu kategorii i dalej, aż dojdziesz do kategorii produktu bez elementu nadrzędnego.

Twoja aplikacja e-commerce będzie musiała wprowadzić to rozróżnienie, ale jeśli mimo to prawdopodobnie załadujesz produkty kategorii, nie jest to strata wydajności, jeśli wiesz, czy masz do czynienia z kategorią lub produktem. Ładnie nadaje się również do wyszukiwania w stylu drzewa do poziomu produktu, ponieważ każdy produkt (kategoria) otworzyłby się na listę podproduktów bez większego nakładu pracy.

Wadą tego są oczywiście dodatkowe informacje obecne w produkcie, które nie mają sensu dla kategorii, powodowałyby niewygodne niewykorzystane pola w produkcie. Chociaż to rozwiązanie byłoby bardziej elastyczne w twojej aplikacji, jest również nieco mniej intuicyjne.

Relacja wiele do wielu

Produkty nie są już w złożonym związku z własnością. Tworzysz tabelę ProductProperty z kluczami obcymi zarówno tabeli produktu, jak i tabeli właściwości, które je łączą. Podobnie masz tabelę kategorii z relacją wiele do wielu z tabelą właściwości i tabelę CategoryProperty z kluczami obcymi zarówno tabeli kategorii, jak i tabeli właściwości.

Sam produkt miałby relację typu „jeden do jednego” z kategorią, umożliwiając zasadniczo utworzenie listy unikalnych właściwości dotyczących zarówno produktu, jak i kategorii za pomocą dobrze sformalizowanej instrukcji select.

Z punktu widzenia bazy danych jest to zdecydowanie czystsze i bardziej elastyczne. Twoja aplikacja prawdopodobnie wykona większość zadań bez bezpośredniego kontaktu z CategoryProperty lub ProductProperty, jeśli zapytanie zostanie wykonane poprawnie. Nie należy jednak traktować kategorii ani produktu jako właściciela nieruchomości. Powinien to być własny podmiot w twoim programie. Oznacza to również, że zarządzanie wymienionymi właściwościami byłoby kwestią utworzenia samej właściwości, a następnie powiązania jej z kategorią lub produktem w dwóch oddzielnych krokach. Z pewnością więcej pracy niż pierwsze rozwiązanie, ale bynajmniej nie trudniejsze.

Oprócz tego będziesz musiał przeprowadzić dodatkową kontrolę po usunięciu kategorii lub produktu, jeśli którakolwiek z jej właściwości jest wykorzystywana przez inne osoby (w przeciwieństwie do pierwszego rozwiązania, w którym możesz bezpiecznie wyeliminować wszystkie powiązane właściwości danego produktu / kategorii) .

Wniosek

W kontekście zawodowym wybrałbym kategorię dodatkowych mil i odległości od produktu i produktu od nieruchomości, stosując podejście wiele do wielu. Nie byłoby możliwości nakładania się danych iw pewnym sensie łatwiej jest uzasadnić każdą z tych trzech dziedzin jako własną całość. Jednak w żadnym wypadku pierwsze rozwiązanie nie jest złe, ponieważ pozwala także napisać prostszą aplikację. Po prostu wiedz, że jeśli pomyślałeś, że w końcu może zajść potrzeba przejścia z jednego rozwiązania na drugie, prawdopodobnie najlepszym rozwiązaniem byłoby wybranie drugiego.

Powodzenia!

Neil
źródło
Dzięki za szczegółową i interesującą odpowiedź! na poziomie bazy danych, którą już modelowałem, jak wyjaśniono w drugim przypadku, ten wzorzec nazywany jest wartością „jednostka-atrybut”, ale utknąłem na poziomie kodu, a mianowicie definicji agregatów. W większości przypadków wszystkie te jednostki są używane razem. można łączyć w jeden agregat, ale zdarzają się przypadki, które lubią zapełnianie katalogów, które w sensie są usuwane z agregatu.
cephei