O ile rozumiem, główną ideą CQRS są 2 różne modele danych do obsługi poleceń i zapytań. Są to tak zwane „model zapisu” i „model odczytu”.
Rozważmy przykład klonowania aplikacji na Twitterze. Oto polecenia:
- Użytkownicy mogą się zarejestrować.
CreateUserCommand(string username)
emitujeUserCreatedEvent
- Użytkownicy mogą obserwować innych użytkowników.
FollowUserCommand(int userAId, int userBId)
emitujeUserFollowedEvent
- Użytkownicy mogą tworzyć posty.
CreatePostCommand(int userId, string text)
emitujePostCreatedEvent
Chociaż używam powyższego terminu „zdarzenie”, nie mam na myśli zdarzeń „pozyskiwania zdarzeń”. Mam na myśli sygnały, które uruchamiają aktualizacje odczytu modelu. Nie mam sklepu z wydarzeniami i do tej pory chcę się skoncentrować na samym CQRS.
A oto pytania:
- Użytkownik musi zobaczyć listę swoich postów.
GetPostsQuery(int userId)
- Użytkownik musi zobaczyć listę swoich obserwujących.
GetFollowersQuery(int userId)
- Użytkownik musi zobaczyć listę obserwowanych użytkowników.
GetFollowedUsersQuery(int userId)
- Użytkownik musi zobaczyć „kanał znajomych” - dziennik wszystkich działań swoich znajomych („Twój przyjaciel John właśnie utworzył nowy post”).
GetFriedFeedRecordsQuery(int userId)
Aby sobie z CreateUserCommand
tym poradzić , muszę wiedzieć, czy taki użytkownik już istnieje. W tym momencie wiem, że mój model zapisu powinien zawierać listę wszystkich użytkowników.
Aby sobie z FollowUserCommand
tym poradzić , muszę wiedzieć, czy użytkownik A już obserwuje użytkownika B, czy nie. W tym momencie chcę, aby mój model zapisu miał listę wszystkich połączeń użytkownik-użytkownik-użytkownik.
I na koniec, CreatePostCommand
nie sądzę, że potrzebuję niczego innego, ponieważ nie mam takich poleceń UpdatePostCommand
. Gdybym je miał, musiałbym upewnić się, że post istnieje, więc potrzebowałbym listy wszystkich postów. Ale ponieważ nie mam tego wymagania, nie muszę śledzić wszystkich postów.
Pytanie nr 1 : czy rzeczywiście jest poprawne użycie terminu „napisz model” w taki sposób, w jaki go używam? Czy też „model zapisu” zawsze oznacza „magazyn zdarzeń” w przypadku ES? Jeśli tak, to czy istnieje jakakolwiek separacja między danymi potrzebnymi do obsługi poleceń a danymi potrzebnymi do obsługi zapytań?
Do obsługi GetPostsQuery
potrzebowałbym listy wszystkich postów. Oznacza to, że mój model odczytu powinien mieć listę wszystkich postów. Zamierzam utrzymać ten model, słuchając PostCreatedEvent
.
Aby obsłużyć jedno GetFollowersQuery
i drugie GetFollowedUsersQuery
, potrzebowałbym listy wszystkich połączeń między użytkownikami. Aby utrzymać ten model, będę go słuchał UserFollowedEvent
. Oto pytanie nr 2 : czy praktycznie jest OK, jeśli użyję tutaj zapisu listy połączeń modelu? A może powinienem stworzyć osobny model do odczytu, ponieważ w przyszłości może potrzebuję więcej szczegółów niż w modelu do pisania?
Wreszcie, aby sobie z tym poradzić GetFriendFeedRecordsQuery
, musiałbym:
- Słuchać
UserFollowedEvent
- Słuchać
PostCreatedEvent
- Dowiedz się, którzy użytkownicy śledzą, którzy inni użytkownicy
Jeśli użytkownik A podąża za użytkownikiem B, a użytkownik B zaczyna podążać za użytkownikiem C, powinny pojawić się następujące rekordy:
- Dla użytkownika A: „Twój znajomy użytkownik B właśnie zaczął obserwować użytkownika C”
- Dla użytkownika B: „Właśnie zacząłeś obserwować użytkownika C”
- Dla użytkownika C: „Użytkownik B obserwuje teraz użytkownika”
Oto pytanie nr 3 : jakiego modelu należy użyć, aby uzyskać listę połączeń? Czy powinienem używać modelu zapisu? Czy powinienem używać modelu odczytu - GetFollowersQuery
/ GetFollowedUsersQuery
? Czy powinienem sprawić, by GetFriendFeedRecordsQuery
sam model obsługiwał UserFollowedEvent
i utrzymywał własną listę wszystkich połączeń?