Given the following CQRS + DDD background:
Aggregate Root:
class Message{Guid guid, string content, Guid toUserGuid, MessageStatus status...}
class User{Guid guid, string name, Email email...}
Command:
CreateMessageCommand{Guid messageGuid, string content, Guid toUserGuid...}
CommandHandler:
CreateMessageCommandHandler<CreateMessageCommand>
=> create a message aggregate root (status set to e.g. "Request Received") and save it through MessageRepository
(The message has not been sent yet. CreateMessageCommandHandler
only create and save the message aggregate root). Then publish a MessageCreatedEvent
Event:
MessageCreatedEvent{Guid messageGuid}
EventHandler:
MessageCreatedEventHandler<MessageCreatedEvent>
=> Get Message
using event.messageGuid
through MessageRepository
=> ??? => emailService.Send(user.email, message.content)
=> status set to "Sent"
My question is, in the MessageCreatedEventHandler<MessageCreatedEvent>
"???" part, am I allowed to get User
using message.toUserGuid
through UserRepository
, for sake of e.g. getting user.email
. Another option would be a domain service UserService
with method GetEmailById(Guid userGuid) => Email
, I think it is better because event handler don't need the entire object of User
, and UserService
can handle all the logic of how to retrieve a User
(e.g. with UserRepository
)
I am not trying to get read model (Query-side) here, I just need some information from aggregate root which is not the current context.
Thank you, I appreciate any comments and answers related to this topic.
TLDR: In CQRS command handler/event handler, how should I get information about aggregate root which is outside my current command/event context.
TLDR: In CQRS command handler/event handler, how should I get information about aggregate root which is outside my current command/event context.
Usual answer: you ask some store for an unlocked copy of the information that you need. That information is data on the outside, meaning that the authoritative copy might be changing while you are working (example: your User is having their email address changed while you are trying to send the message).
Mechanically, that will usually look like having some "service" that accepts the identifier and returns the information you need (think "repository" but without the capability to change the data).
Depending on context, you might invoke that service from your "application code", and pass the answer to your "domain code", or you might pass the service itself to the domain code (this is the domain service pattern).
It's relatively straight forward to get the machine to do the Right Thing in either case; the choice is mostly one of design tradeoffs (example: copying data from a remote source can fail - do you want error handling mixed in with your domain code?)