Наскільки я розумію, великою ідеєю CQRS є наявність двох різних моделей даних для обробки команд та запитів. Вони називаються "модель запису" та "модель читання".
Розглянемо приклад клонування програми Twitter. Ось команди:
- Користувачі можуть зареєструватися.
CreateUserCommand(string username)
випускаєUserCreatedEvent
- Користувачі можуть стежити за іншими користувачами.
FollowUserCommand(int userAId, int userBId)
випускаєUserFollowedEvent
- Користувачі можуть створювати повідомлення.
CreatePostCommand(int userId, string text)
випускаєPostCreatedEvent
Хоча я використовую термін "подія" вище, я не маю на увазі події "пошук подій". Я просто маю на увазі сигнали, які викликають читання оновлень моделі. У мене немає магазину подій, і поки що хочу зосередитися на самому CQRS.
А ось запити:
- Користувачеві потрібно переглянути список своїх публікацій.
GetPostsQuery(int userId)
- Користувачеві потрібно переглянути список його підписників.
GetFollowersQuery(int userId)
- Користувачеві потрібно побачити список користувачів, який він випливає.
GetFollowedUsersQuery(int userId)
- Користувачеві потрібно бачити "канал друзів" - журнал усіх дій їхніх друзів ("твій друг Джон щойно створив нову публікацію").
GetFriedFeedRecordsQuery(int userId)
Для обробки CreateUserCommand
мені потрібно знати, чи такий користувач вже існує. Отже, на даний момент я знаю, що моя модель запису повинна мати список усіх користувачів.
Для обробки FollowUserCommand
мені потрібно знати, чи користувачA вже переглядає userB чи ні. На даний момент я хочу, щоб моя модель запису мала список усіх підключень користувачів, які слідкують за користувачами.
І, нарешті, для обробки CreatePostCommand
я не думаю, що мені більше потрібне, тому що у мене немає таких команд UpdatePostCommand
. Якби у мене були такі, мені потрібно було б переконатися, що повідомлення існує, тому мені знадобиться список усіх публікацій. Але оскільки у мене немає цієї вимоги, мені не потрібно відстежувати всі повідомлення.
Запитання №1 : чи правильно використовувати термін "модель запису", як я його використовую? Або "запис моделі" завжди означає "магазин подій" у випадку ES? Якщо так, чи існує якийсь поділ між даними, які мені потрібні для обробки команд, та даними, які мені потрібні для обробки запитів?
Для обробки GetPostsQuery
мені знадобиться список усіх постів. Це означає, що моя модель читання повинна містити список усіх публікацій. Я буду підтримувати цю модель, слухаючи PostCreatedEvent
.
Для обробки обох GetFollowersQuery
і GetFollowedUsersQuery
мені знадобиться список усіх з'єднань між користувачами. Для підтримки цієї моделі я буду слухати UserFollowedEvent
. Ось питання № 2 : чи практично це нормально, якщо тут я використовую список моделей записів? Або мені краще створити окрему модель читання, тому що в майбутньому мені може знадобитися більше деталей, ніж модель запису?
Нарешті, щоб впоратися, GetFriendFeedRecordsQuery
мені потрібно:
- Слухати
UserFollowedEvent
- Слухати
PostCreatedEvent
- Знайте, які користувачі слідкують за іншими користувачами
Якщо користувач A слідкує за користувачем B, а користувач B починає слідкувати за користувачем C, повинні з'являтися такі записи:
- Для користувача A: "Ваш друг B користувач щойно почав слідкувати за користувачем C"
- Для користувача B: "Ви тільки почали слідкувати за користувачем C"
- Для користувача C: "Користувач B тепер стежить за вами"
Ось питання №3 : яку модель я повинен використовувати для отримання списку підключень? Чи варто використовувати модель запису? Чи варто використовувати модель читання - GetFollowersQuery
/ GetFollowedUsersQuery
? Або я повинен змусити GetFriendFeedRecordsQuery
модель сама обробляти UserFollowedEvent
та підтримувати свій власний список всіх підключень?