Pelo que entendi, a grande idéia por trás do CQRS é ter dois modelos de dados diferentes para manipular comandos e consultas. Eles são chamados de "modelo de gravação" e "modelo de leitura".
Vamos considerar um exemplo de clone de aplicativo do Twitter. Aqui estão os comandos:
- Os usuários podem se registrar.
CreateUserCommand(string username)
emiteUserCreatedEvent
- Os usuários podem seguir outros usuários.
FollowUserCommand(int userAId, int userBId)
emiteUserFollowedEvent
- Os usuários podem criar postagens.
CreatePostCommand(int userId, string text)
emitePostCreatedEvent
Embora eu use o termo "evento" acima, não me refiro a eventos 'sourcing de eventos'. Quero dizer apenas sinais que acionam atualizações de modelo de leitura. Eu não tenho uma loja de eventos e até agora quero me concentrar no CQRS.
E aqui estão as perguntas:
- Um usuário precisa ver a lista de suas postagens.
GetPostsQuery(int userId)
- Um usuário precisa ver a lista de seus seguidores.
GetFollowersQuery(int userId)
- Um usuário precisa ver a lista de usuários a seguir.
GetFollowedUsersQuery(int userId)
- Um usuário precisa ver o "feed de amigos" - um log de todas as atividades de seus amigos ("seu amigo John acabou de criar uma nova postagem").
GetFriedFeedRecordsQuery(int userId)
Para lidar CreateUserCommand
, preciso saber se esse usuário já existe. Portanto, neste momento eu sei que meu modelo de gravação deve ter uma lista de todos os usuários.
Para lidar FollowUserCommand
, preciso saber se o usuário A já segue o usuárioB ou não. Neste ponto, quero que meu modelo de gravação tenha uma lista de todas as conexões usuário-usuário-usuário.
E, finalmente, para lidar com CreatePostCommand
isso, acho que não preciso de mais nada, porque não tenho comandos como UpdatePostCommand
. Se eu os tivesse, precisaria garantir que a postagem exista, portanto, precisaria de uma lista de todas as postagens. Mas como não tenho esse requisito, não preciso rastrear todas as postagens.
Pergunta # 1 : é realmente correto usar o termo "modelo de gravação" da maneira que eu o uso? Ou "modelo de gravação" sempre significa "armazenamento de eventos" no caso de ES? Em caso afirmativo, existe algum tipo de separação entre os dados necessários para manipular comandos e os dados necessários para manipular consultas?
Para lidar GetPostsQuery
, eu precisaria de uma lista de todas as postagens. Isso significa que meu modelo de leitura deve ter uma lista de todas as postagens. Vou manter esse modelo ouvindo PostCreatedEvent
.
Para lidar com ambos GetFollowersQuery
e GetFollowedUsersQuery
, eu precisaria de uma lista de todas as conexões entre usuários. Para manter esse modelo, vou ouvir UserFollowedEvent
. Aqui está uma pergunta 2 : é praticamente bom se eu usar a lista de conexões do modelo de gravação aqui? Ou devo criar melhor um modelo de leitura separado, porque no futuro posso precisar de mais detalhes do que o modelo de gravação?
Finalmente, para lidar, GetFriendFeedRecordsQuery
eu precisaria:
- Ouvir
UserFollowedEvent
- Ouvir
PostCreatedEvent
- Saiba quais usuários seguem quais outros usuários
Se o usuário A seguir o usuário B e o usuário B começar a seguir o usuário C, os seguintes registros deverão aparecer:
- Para o usuário A: "Seu amigo usuário B acabou de começar a seguir o usuário C"
- Para o usuário B: "Você começou a seguir o usuário C"
- Para o usuário C: "O usuário B agora está seguindo você"
Aqui está a pergunta nº 3 : qual modelo devo usar para obter a lista de conexões? Devo usar o modelo de gravação? Devo usar o modelo de leitura - GetFollowersQuery
/ GetFollowedUsersQuery
? Ou devo fazer o GetFriendFeedRecordsQuery
próprio modelo manipular UserFollowedEvent
e manter sua própria lista de todas as conexões?