The only reason a CQRS application should fetch some information (I don't call it a "Query", it would be misleading) in the middle of the command path, is that a use case needs to retrieve information from the outside world before to validate a command. The outisde world typically is another bounded context, so this means that, when you do that, you are calling a microservice, a 3rd party api, or even a method defined in a different module of you monolith
public void purchase(String orderId , String promocode) {
boolean isValidPromocode = promocodeDomainService.check(promocode);
commandGateway.send(new PurchaseOrderCommand(orderId , isValidPromocode));
}
This assumes that promocodes are managed from another service. After all, purchases are managed from sales/inventory/shimpent functions, while promocodes are a marketing concept, so it makes sense that are not part of the aggregate that performs the PurchaseOrderCommand
What happens behind the curtains is not our business. The domain service here is just an abstraction
Aside from this case, you aggregate must have everything it needs to validate its invariants
if (eventSourcing == false)
In this case, the only statement that your (write) DB will process, would be that one which loads the aggregate. Before saving back your aggregate
public void purchase(String orderId , String promocode) {
Order = repository.find(orderId);
boolean isValidPromocode = promocodeDomainService.check(promocode);
order.apply(new PurchaseOrderCommand(orderId , isValidPromocode));
repository.save(order);
}
else
The in-memory representation of the aggregate is updated as domain messages are published or retrived from the event store
In any case, on the query side, if you need some data that orginal aggregate doesn't own, you can still query external services or provide an aggregation layer in front of your projection