In distributed systems (eg. micro-services, SOA) communication between services should be performed through messages. But what is a "message"? Are there different kinds for different purposes?
Let’s see.
Query
A query describes a request for information.
Transport
As the requester needs the information in a timely manner, they’re usually sent over synchronous protocols like HTTP or gRPC.
Anatomy
A query consists of a request and a response.
Request
A request contains all information to filter the available data.
Usual contents are
- ID of an entity
- Page and page-size for paging of list-results
- Filter values like a search-term
Response
A response contains the requested information, or – in case of an error – the reason why the query could not be fulfilled.
The usual reasons are:
- No user identification provided although required (in HTTP status code 401)
- Insufficient permissions (in HTTP status code 403)
- Entity not found (in HTTP status code 404)
Key Points
- Does return the result of the query to the initiator
- Does not modify any state
- "Side-effect free"
- Multiple queries do not cause a change in the response
- Can be rejected due to insufficient permissions
Command
A command message describes a request for change.
It has *exactly 1 logical receiver**. The receiver can decide if the command is valid and can be executed.
*) By "logical" I mean 1 logical dedicated software component. This component can be deployed multiple times in parallel, as long as only 1 of them handles the command ("competing consumers").
Transport
Commands can be sent synchronous or asynchronous. If sent synchronous, HTTP or gRPC are commonly used. If sent asynchronous, a message-bus takes care of durable transport.
Choosing Transport
If synchronous transport protocol is used, temporal coupling is the implicit result (which is bad). What that means is, that if the receiver is not available (restarting, temporarily down,…) the command will fail.
To solve this asynchronous transport is advised as it avoids temporal coupling. If the receiver is not available, the message-bus stores it durably. And as soon as the receiver comes back up again, it gets the command handed over to be processed.
Anatomy
A command contains all information required to fulfill a change.
The naming of the command itself should express, that it is an order. Also it is phrased in present tense.
Eg. AddProductToCart
.
Key Points
- Expresses a request for changing something
- Does modify state
- Is not side-effect free
- Can be rejected
- Due to insufficient permissions
- Because it is not allowed in the current state (but may be fine previously or later on)
Event
An event message describes a change in the past. So it cannot be rejected by anyone, as you cannot "undo" history.
It has 0 to infinite receivers, therefore it is called "publishing" and not "sending" an event.
Events do have an notification character. The event publisher does not know if and when an event is processed, or who the recipients are. This is a perfect example for loose coupling.
Transport
Due to its loosely coupled character, events are transported asynchronously via a message-bus.
A message-bus allows interested parties to subscribe to a particular type of event. When an event is published, each subscriber gets their own copy in their inbox.
Anatomy
An event contains all information describe what happened, respectively what changed.
The naming of the command itself should express, that it already happened. Therefore it is phrased in past tense.
Eg. ProductAddedToCart
.
Key Points
- Expresses some change that already happened
- Does not modify state (but was created due to change)
- Can not be rejected
- "The past cannot be rejected"
Summary
- Queries request data and cause data to be returned to the requester
- Commands request a change, but can be rejected
- Events describe some change in the past, therefore it cannot be rejected
Recent Comments