Jak należy obsługiwać polecenia Dodaj / Utwórz * w architekturze CQRS + Event Sourcing

11

Chcę wdrożyć moją pierwszą aplikację przy użyciu wzorca CQRS wraz z Event Sourcing. Zastanawiam się, jak prawidłowo obsługiwać tworzenie zagregowanych korzeni. Powiedzmy, że ktoś wysyła polecenie CreateItem. Jak sobie z tym poradzić? Gdzie należy przechowywać zdarzenie ItemCreated? Jako pierwsze wydarzenie nowego przedmiotu? Czy powinienem mieć jakiś element ItemList, który agreguje wszystkie elementy, a lista zdarzeń zawiera tylko zdarzenia ItemCreated?

Udi Dahan sugeruje, aby nie tworzyć zagregowanych korzeni i zawsze używać zamiast tego jakiejś metody pobierania. Ale jak mogę pobrać coś nowego i na pewno nie ma przypisanego żadnego identyfikatora. Rozumiem tę ideę i rozsądnie jest myśleć, że nowy obiekt to obiekt, którego stan składa się z odpowiedzi na zero zdarzeń. Ale jak mam go używać? Czy powinienem mieć w moim repozytorium odrębną metodę, taką jak getNewItem()lub uczynić moją get(id)metodę akceptującą Optional<ItemId>?

Edycja: Po pewnym czasie kopania znalazłem naprawdę ciekawą implementację wyżej wymienionych wzorców za pomocą aktorów. Autor zamiast tworzyć agregat pobiera go z pewnego rodzaju repozytorium z nowo utworzonym UUID. Wadą tego podejścia jest to, że pozwala on na przejściowy stan niespójności. Zastanawiam się także, jak mogę wdrożyć deletemetodę z takim podejściem. Po prostu dodać usunięte wydarzenie do listy zdarzeń agregatu?

Mequrel
źródło
1
Podejrzewam, że post Udi wprowadza w błąd. IMHO wydaje się, że jego prawdziwym celem jest, aby świeżo wykonane AR były zawsze dostępne z innego miejsca, w sposób, który oddaje kontekst, dlaczego / jak / kto zdecydował, że należy stworzyć nowe AR. Wszystko inne dotyczy tego, jak określona implementacja (NHibernate?) Może ułatwić zarządzanie.
Darien
2
Zwróć uwagę, że artykuł Udi Dahan, do którego się odwołujesz, wyraźnie mówi, że jego rada może nie mieć zastosowania do pozyskiwania wydarzeń: udidahan.com/2009/06/29/dont-create-aggregate-roots/…
EZ Hart

Odpowiedzi:

13

Z tego, co rozumiem, w postie Udi jest to, że żaden przedmiot nie pojawia się z powietrza. Zawsze jest (prawie) zawsze coś, a dokładniej, pewne działanie domeny, które spowodowało utworzenie elementu. Podobnie jak w przypadku Udi, że użytkownik faktycznie rodzi się z odwiedzającego rejestrującego się na stronie. W tym momencie i w tym ograniczonym kontekście Odwiedzający jest zagregowanym rootem, który jest pobierany przez jego adres IP. Ten gość następnie tworzy nowy „element”, w tym momencie użytkownika, poprzez operację domeny o nazwie Zarejestruj . To samo dotyczy kroku wcześniejszego, który jest innym ograniczonym kontekstem: strona odsyłająca to AR, który jest pobierany przez adres URL i który ma operację domeny o nazwie BroughtVisitorWithIp , w której urodził się użytkownik.

Udi również bardzo ładnie pisze o usuwaniu: http://www.udidahan.com/2009/09/01/dont-delete-just-dont/ . Główną ideą jest to, że nigdy niczego nie usuwasz. Za nami zawsze jest operacja domeny, którą chcemy uchwycić. Jak zamówienie anulowane, a nie usuwane. Przeczytaj, to bardzo dobry post.

Głównym punktem na obu kontach, wykonując DDD, a zwłaszcza Sourcing zdarzeń, jest to, że nigdy nie powinieneś wykonywać prostych operacji CRUD. Jeśli znajdziesz się w sytuacji, w której naprawdę musisz po prostu wstawić, zaktualizować lub usunąć niektóre dane, a naprawdę nie ma za tym operacji domeny, być może DDD i Event Sourcing nie są odpowiednie dla tego ograniczonego kontekstu . Możesz dowolnie łączyć te dwa elementy tak długo, jak długo jeden kontekst ogranicza się do jednej zasady. W ten sposób kontekst ograniczony w stylu CRUD może utworzyć wiersz w bazie danych, który stanie się bytem i agregatem głównym w innym ograniczonym kontekście, w którym można teraz pobrać AR i nie trzeba go tworzyć.

Tuukka Haapaniemi
źródło
2
„może DDD i Event Sourcing nie są odpowiednie do tego ograniczonego kontekstu.” Masz rację w DDD. Nie należy go wdrażać w każdym przypadku tylko dla chwały szatana, ale tylko wtedy, gdy trzeba poradzić sobie ze złożoną domeną pełną niepewnych zasad. Osobiście zrobiłem to dla legalnego oprogramowania, w którym wymagania nie są uzależnione od logiki.
Jegor Czumakow
2
+1 dla samego zdania „dla tego ograniczonego kontekstu”. :)
Songo,
2
+1 użycie czasowników „Dodaj” i „Utwórz” zdecydowanie sugeruje, że nadal myślisz o swojej domenie w kategoriach interakcji ze starą, dobrą tabelaryczną bazą danych. Bez znajomości Twojej domeny / określonego kontekstu nie mogę powiedzieć, czy jest to właściwe, czy nie. Zignoruj ​​wytrwałość, najpierw skup się na KOMENDACH i WYDARZENIACH (czyli INTENCJACH i WYNIKACH), które są unikalne dla Twojej domeny, a następnie martw się, jak zachować stan, który jest problemem, który został rozwiązany setki tysięcy razy wcześniej.
Matt
„użycie czasowników„ Dodaj ”i„ Utwórz ”zdecydowanie sugeruje, że nadal myślisz o swojej domenie w kategoriach interakcji ze starą, dobrą tabelaryczną bazą danych” Hmmm. Jeśli masz projekt interfejsu użytkownika z dużym przyciskiem „Dodaj coś”, to niestety taka jest intencja; dosłownie, aby dodać coś nowego. Generalnie się z tobą zgadzam, ale nie mówimy tutaj o poziomie bazy danych, czasami Dodaj lub Utwórz to właściwie właściwe słowa do użycia.
designermonkey
1
@designermonkey Kiedy masz te przyciski w interfejsie, czy naprawdę masz za nimi operację domeny? Być może, ale 9 na 10 razy tak naprawdę nie ma potrzeby złożonej operacji na domenie w tym ograniczonym kontekście. A operacja czystego CRUD jest po prostu operacją czystą CRUD i powinna być traktowana jako taka. Należy go stosować tylko wtedy, gdy istnieje potrzeba złożoności modelu domeny. Zatem różne granice kontekstów z różnymi zasadami projektowania.
Tuukka Haapaniemi