I think it is important to keep in mind that the primary difference between a Command
and an Event
is semantics.
Hohpe:
Messages are ultimately just bundles of data, but the sender can have different intentions for what it expects the receiver to do with the message. It can send a Command Message specifying a function or method on the receiver that the sender wishes to invoke.... it can send an Event Message, notifying the receiver of a change in the sender.
CommandReceived
is an event; handleEvent
is a command.
A key distinction between the two is this
- commands travel toward the authority for some given state
- events travel away from the authority for some given state
With that in mind, let's take a look at the players
Aggregates are a component of the domain model, responsible for ensuring that changes to the domain state satisfy the business invariant. The domain model's reason for existing at all is to stand as the authority for that state. So we would expect messages sense to the domain model (and therefore, to the aggregate) to be commands.
Process managers don't actually interact with domain state directly. They are orchestration components that react to messages from other authorities. So we would expect these incoming messages to be events. In reacting to these messages, process managers are usually sending messages to domain models (or other facilities, like email gateways). These outbound messages would be commands.
Which negative consequences would an aggregate have, that directly consumes events produced by another aggregate?
"directly" can mean a lot of different things here. Conceptually, there's nothing wrong with a command like
void placeOrder( OrderFormSubmitted event )
Where things get interesting is figuring out whether or not to include different messages within the same transaction. Udi Dahan's discussion of domain events uses event handlers that run in the same thread / same transaction as the initial command; so the entire orchestration is going to succeed or fail together.
Furthermore, because you really want to avoid two-phase commit if you can possibly help it, you really need to be sure that all of the aggregates involved in the orchestration can participate in the same transaction (ie: are stored in the same database).
More recent writings tend to assume that the aggregates may be distributed, in which case you really want to separate the transactions.
Typically, if you are going to be doing an orchestration across multiple bounded contexts, you are going to want separate transactions anyway.
An additional problem with handling events "directly" is preventing changes to your messages from forcing a cascading upgrade across your system. Greg Young's Versioning in an Event Sourced System is a great read, but the high level summary is that events are messages from the old model to the new, so you should be investing early design capital in ensuring that your message schema are designed with change support in mind.