Let's start with a short review of the problem-space here. The fundamental benefit of adopting a CQRS pattern is to solve/simplify your problem domain by reducing the interleaving and leakage that begins to occur when utilizing the same model for your write-side as your read-side. Often, the tension that arises serves as a detriment to both. CQRS seeks to relieve this tension by separating (decoupling logically and possibly physically) the write-side and read-side of your system. With the above in mind it should be clear that neither your commands nor queries should be coupled to a logical entity from the other "side".
Given the above, we can now formulate a direct answer to your question: Yes, you can query your data store within a command handler provided the query is issued against your command model. Because your OrderCreateCommandHandler
is part of the command model of your application, we want to avoid coupling it to any part of your read model. It's unclear whether or not this is case given the example code (although the name CustomerQuery
does raise some suspicions).
More important than the answer above though is that... there is something else that feels fishy about the example you have provided. Can you feel that too?
What I see here is quite a bit of coupling. Your handler is retrieving a CustomerResult
(VO?), then breaking down all of it's data into another entity's constructor (Customer
), then passing the Customer
to a factory method of yet another entity. We have quite a bit of "asking" happening here. That is, we are passing around a lot of data in way that creates coupling.
Furthermore, the command handler doesn't "read" in a very declarative fashion (which is what we want to strive for). What I mean is that it's kind of hard to "see" what's happening in your method because there is so much plumbing getting in the way. I think we can come up with a more cohesive/declarative solution.
Given that the general "flow" of a command handler can be broken down into three simple steps:
- Retrieve all data (domain model) necessary to carryout the use-case
- Coordinate the data to fulfill the use-case
- Persist the data
Let us see if we can come up with a simpler solution:
buyer = buyers.Find( cmd.CustomerId );
buyer.PlaceOrder( cmd.Products );
buyers.Save( buyer );
Ah ha! Much cleaner (3 simple steps). More importantly though, not only does the code above achieve your same goal, it does so without creating many dependencies between disparate objects as wells as functioning in a more declarative and encapsulated manner (we aren't "newing" anything or calling any factory methods)! Let's break this down piece by piece so we can understand "why" the above may be a better solution.
buyer = buyers.Find( cmd.CustomerId );
The first thing I've done is introduce a new concept: Buyer
. In so doing this, I am partitioning your data vertically according to behavior. Let's let your Customer
entity have responsibility for maintaining Customer
information (FirstName
, LastName
, Email
, etc.), and allow a Buyer
to be responsible for making purchases. Because some Customer
information needs to be recorded when a purchase is made, we will hydrate a Buyer
with a "snapshot" of that data (and possibly other data).
buyer.PlaceOrder( cmd.Products );
Next we coordinate the purchase. The above method is where a new Order
is created. An Order
doesn't just appear out of nowhere right? Something must place it, so we model accordingly. What does this achieve? Well, the Buyer.PlaceOrder
method provides a place in your domain to throw BuyerNotInGoodStanding
, OrderExceedsBuyerSpendingLimit
, or RepeatOrderDetected
exceptions. By only creating an Order
in the context of it's placement, we can enforce how an Order
can come about. In your example, either your application-layer command handler or your Order
factory method would have to be made responsible for enforcing each invariant. Neither is a good place for checking business rules. Additionally we now have a place to raise our OrderPlaced
event (which will be necessary to keep your payment context decoupled), and also we can simplify your Order
entity as it now only needs a scalar buyerId
to keep reference to it's owner.
buyers.Save( buyer );
Pretty self-explanatory. A Buyer
now contains all of the information you need to persist both an Order
and a "snapshot" of Customer
data. How you organize that data internally and take it apart for persistence is up to you (hint: A Buyer
needn't be persisted at all, for example. Just the Order
it contains).
EDIT
The example solution (if we can call it that) that I posted is one meant to get the "gears turning", and doesn't necessarily represent the best-possible solution to the problem at hand. That is, your problem. It is totally possible (even likely) that introducing the concept of a Buyer
aggregate is over-engineering given that there had been no mention of any sort of rules regarding how an Order
can be placed. For example:
customer = customers.Find( cmd.CustomerId );
order = customer.PlaceOrder( cmd.Products ); // raise OrderPlaced
orders.Save( order );
may be a totally valid approach! Just be sure to include all of the necessary information in the CustomerInformationSlip
(your "snapshot") attached to the Order
to allow it to enforce any invariant controlling how it can be modified. For example:
order.ChangeShippingAddress( cmd.Address ); // raise ShippingAddressChanged
The above may throw an OrderExceedsCustomerShippingDistance
if each Customer
has their own rules regarding how far you will ship to them given their account tier.
Let the rules dictate the design!
CustomerRepository#findById(id: CustomerId): Customer
is completely fine. Whether you should also be able to query your read side is for much longer discussion and depends on many variables, therefore I'm not going to go into the detail here.