Насколько я понимаю, основная идея 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
мне нужно знать, следует ли userA уже за 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
и поддерживать свой собственный список всех соединений?