As far as I understand, the big idea behind CQRS is having 2 different data models for handling commands and queries. These are called "write model" and "read model".
Let's consider an example of Twitter application clone. Here are the commands:
- Users can register themselves.
CreateUserCommand(string username)emitsUserCreatedEvent - Users can follow other users.
FollowUserCommand(int userAId, int userBId)emitsUserFollowedEvent - Users can create posts.
CreatePostCommand(int userId, string text)emitsPostCreatedEvent
While I use the term "event" above, I don't mean 'event sourcing' events. I just mean signals that trigger read model updates. I don't have an event store and so far want to concentrate on CQRS itself.
And here are the queries:
- A user needs to see the list of its posts.
GetPostsQuery(int userId) - A user needs to see the list of its followers.
GetFollowersQuery(int userId) - A user needs to see the list of users it follows.
GetFollowedUsersQuery(int userId) - A user needs to see the "friend feed" - a log of all their friends' activities ("your friend John has just created a new post").
GetFriedFeedRecordsQuery(int userId)
To handle CreateUserCommand I need to know if such a user already exists. So, at this point I know that my write model should have a list of all users.
To handle FollowUserCommand I need to know if userA already follows userB or not. At this point I want my write model to have a list of all user-follows-user connections.
And finally, to handle CreatePostCommand I don't think I need anything else, because I don't have commands like UpdatePostCommand. If I had those, I would need to make sure that post exists, so I would need a list of all posts. But because I don't have this requirement, I don't need to track all posts.
Question #1: is it actually correct to use the term "write model" they way I use it? Or does "write model" always stand for "event store" in case of ES? If so, is there any kind of separation between the data I need to handle commands and the data I need to handle queries?
To handle GetPostsQuery, I would need a list of all posts. This means that my read model should have a list of all posts. I'm going to maintain this model by listening to PostCreatedEvent.
To handle both GetFollowersQuery and GetFollowedUsersQuery, I would need a list of all connections between users. To maintain this model I'm going to listen to UserFollowedEvent. Here is a Question #2: is it practically OK if I use write model's list of connections here? Or should I better create a separate read model, because in the future I may need it to have more details than write model has?
Finally, to handle GetFriendFeedRecordsQuery I would need to:
- Listen to
UserFollowedEvent - Listen to
PostCreatedEvent - Know which users follow which other users
If user A follows user B and user B starts to follow user C, the following records should appear:
- For user A: "You friend user B has just started following user C"
- For user B: "You've just started following user C"
- For user C: "User B is now following you"
Here's the Question #3: what model should I use to get the list of connections? Should I use write model? Should I use read model - GetFollowersQuery/GetFollowedUsersQuery? Or should I make GetFriendFeedRecordsQuery model itself handle the UserFollowedEvent and maintain its own list of all connections?