Distributed systems are constructed from different bounded contexts. These subsystems each have their own dialect of the ubiquitous language, and are owned by different teams. An enterprise service bus lets you integrate these bounded contexts using messages. Messages are a fantastic form of integration, because they tend to be small, complete, and temporally decoupled. But to effectively integrate using messages, you need to understand what kind of messages you can use.
Kinds of messages
A distributed system using a message-driven architecture will have three kinds of messages:
Each kind of message is intended to flow between bounded contexts in a certain way.
A command is a message that is intended to change the system in some way. Commands are sent asynchronously. They are sent durably. And they carry a complete payload. Commands are named as imperative verb phrases using words like “place” or “process”.
An order placed by a customer is an example of a command. The command carries with it all of the items that the customer wishes to order. There is no guarantee that the command will be successful, but there is a guarantee that it will be processed. We can make this guarantee by adhering to the rules of durability. The command will be received, it will be processed, and it will not be duplicated. We just can’t make any guarantees about the outcome of the command. It may result in the order being shipped. Or it may result in a refund and an apologetic email.
A query is a message that makes no change to the system. Instead, it returns results. Queries are sent synchronously. They are not sent durably. And they carry a specification. Queries are named as imperative verb phrases using words like “get”, “check”, or “list”.
For example, a customer can query for the status of a previously submitted order. The query doesn’t change anything. It is safe to query the status again and again. Because of this, queries don’t have to be sent durably. The payload of the query is a set of criteria for the desired results. These criteria collectively are called a specification. In the case of an order status query, the specification could be nothing more than the order ID.
An event is a notification message that is multi-cast to several interested parties. Events are sent asynchronously. They are sent durably. And they carry a complete payload. what distinguishes an event from a command is the fact that the sender does not have any particular intent for the subsequent change to the system. An event is named as a past tense verb phrase.
The fulfillment system will notify any interested party when an order ships. It multi-casts the order shipped event asynchronously to all subscribers. It must send these messages durably, because they may have a downstream impact on the system. The event contains all of the information that a subscriber might need. This includes the contents of the order, information about the customer, and tracking information for the shipment. The publisher has no way of knowing which pieces of information will be important to the subscribers, so it has to send everything.
Dependencies among bounded contexts
One of the best features of an enterprise service bus is the way that it manages dependencies among bounded contexts. The dependencies in an ESB do not necessarily flow in the same direction as the information. The direction of the dependency is determined by the system that defines it. It is not determined by the direction in which the information flows.
Commands are defined by the information consumer. The producer sends the message to the consumer. The sender intends to make a certain change to a system. It needs to express that intent using the language of the consumer. Senders take a dependency on consumers. They know the data types defined by the command handler, as well as the address of the handler.
Queries are defined by the information producer. The consumer calls an RPC to request information from the producer. The caller provides a specification using the language of the producer. The producer will respond using data types that it has defined. Even though the information is flowing back toward the consumer, the direction of dependency is toward the producer.
Events are defined by the information producer. The consumer subscribes to events from the producer. The subscriber makes the request using the language of the publisher. The subscriber knows the address of the publisher, not the other way around. The publisher will multi-cast events using its own data types. So even though the data is flowing toward the subscriber, the dependency is toward the publisher.
By decoupling the direction of dependency from the direction of information flow, an enterprise service bus lets information flow in many directions without forming circular dependencies. The dependency direction is best optimized for collaboration among bounded contexts created by different teams, hosted on different hardware, and deployed on different schedules. The ESB manages those dependencies and keeps the information flowing.
For more information about CQRS, durability, and enterprise service busses, please watch CQRS Theory and Practice on Pluralsight.