Per quanto ho capito, l'idea alla base di CQRS consiste nel disporre di 2 diversi modelli di dati per la gestione di comandi e query. Questi sono chiamati "modello di scrittura" e "modello di lettura".
Consideriamo un esempio di clone dell'applicazione Twitter. Ecco i comandi:
- Gli utenti possono registrarsi.
CreateUserCommand(string username)
emetteUserCreatedEvent
- Gli utenti possono seguire altri utenti.
FollowUserCommand(int userAId, int userBId)
emetteUserFollowedEvent
- Gli utenti possono creare post.
CreatePostCommand(int userId, string text)
emettePostCreatedEvent
Mentre uso il termine "evento" sopra, non intendo eventi di "event sourcing". Intendo solo segnali che attivano la lettura degli aggiornamenti del modello. Non ho un negozio di eventi e finora voglio concentrarmi su CQRS stesso.
E qui ci sono le domande:
- Un utente deve vedere l'elenco dei suoi post. %codice%
- Un utente deve vedere l'elenco dei suoi follower. %codice%
- Un utente deve vedere l'elenco degli utenti che segue. %codice%
- Un utente deve vedere il "feed degli amici", un registro delle attività di tutti i suoi amici ("il tuo amico John ha appena creato un nuovo post"). %codice%
Per gestire GetPostsQuery(int userId)
Ho bisogno di sapere se un tale utente esiste già. Quindi, a questo punto, so che il mio modello di scrittura dovrebbe avere un elenco di tutti gli utenti.
Per gestire GetFollowersQuery(int userId)
Devo sapere se l'utenteA segue già l'utente B o no. A questo punto voglio che il mio modello di scrittura abbia un elenco di tutte le connessioni utente-utente-seguito.
E infine, per gestire GetFollowedUsersQuery(int userId)
non penso di aver bisogno di nient'altro, perché non ho comandi come GetFriedFeedRecordsQuery(int userId)
. Se ne avessi bisogno, dovrei assicurarmi che il post esista, quindi avrei bisogno di un elenco di tutti i post. Ma poiché non ho questo requisito, non ho bisogno di tenere traccia di tutti i post.
Domanda n. 1 : è effettivamente corretto utilizzare il termine "modello di scrittura" per come lo uso? Oppure "scrivere modello" significa sempre "negozio di eventi" in caso di ES? In tal caso, esiste un qualche tipo di separazione tra i dati di cui ho bisogno per gestire i comandi e i dati che devo gestire per le query?
Per gestire CreateUserCommand
, avrei bisogno di un elenco di tutti i post. Ciò significa che il mio modello di lettura dovrebbe avere un elenco di tutti i post. Ho intenzione di mantenere questo modello ascoltando FollowUserCommand
.
Per gestire sia CreatePostCommand
che UpdatePostCommand
, avrei bisogno di un elenco di tutte le connessioni tra gli utenti. Per mantenere questo modello, ascolterò GetPostsQuery
. Ecco una domanda n. 2 : è praticamente OK se utilizzo qui l'elenco delle connessioni del modello di scrittura? O dovrei creare un modello di lettura separato, perché in futuro potrei aver bisogno di avere più dettagli di quanti ne abbia il modello di scrittura?
Infine, per gestire PostCreatedEvent
avrei bisogno di:
- Ascolta
GetFollowersQuery
- Ascolta
GetFollowedUsersQuery
- Scopri quali utenti seguono quali altri utenti
Se l'utente A segue l'utente B e l'utente B inizia a seguire l'utente C, dovrebbero apparire i seguenti record:
- Per l'utente A: "L'utente amico B ha appena iniziato a seguire l'utente C"
- Per l'utente B: "Hai appena iniziato a seguire l'utente C"
- Per l'utente C: "L'utente B ti sta seguendo ora"
Ecco la domanda n. 3 : quale modello dovrei utilizzare per ottenere l'elenco delle connessioni? Dovrei usare il modello di scrittura? Dovrei usare read model - UserFollowedEvent
/ GetFriendFeedRecordsQuery
? O dovrei rendere UserFollowedEvent
il modello stesso a gestire PostCreatedEvent
e mantenere il proprio elenco di tutte le connessioni?