Microservices
Reactive microservices: Part 5 - event-based messaging with CQRS
Choosing the right message for the job
All of the previous is not to say that one size fits all. When designing a reactive system, you want to choose the best messaging pattern for the purpose. To analyze message needs, it is helpful to categorize the contents as queries, commands, or facts:
- Queries often require a response in a timely fashion. For example, Fred uses an ATM to find the balance on his checking account. He expects a response and if he doesn’t receive one, he wants to know why. Synchronous messaging meets this objective.
- Commands are requests for another service to do something, where the requestor usually needs an answer or an acknowledgment. For example, Fred initiates an ATM withdrawal of one hundred dollars from his checking account. He wants his money now, and if it isn’t forthcoming, he again wants to know why. A slightly different case might be when Fred changes his PIN number online, he needs to know whether it succeeded. Both of these use cases also motivate some type of synchronous communication.
- Events carry or represent historical facts that cannot be changed. Asynchronous messaging is the most efficient and robust way to communicate them. To continue our example, when Fred receives his money, it is a fact that he withdrew one hundred dollars from his checking account. He can redeposit the money, spend it, or lose it, but that doesn’t change the withdrawal event.
Asynchronous messaging provides a simple scalable pattern that you should take advantage of whenever possible. However, synchronous messaging can — and should — be accomplished as efficiently as possible. For example, the sender might wait for a simple acknowledgment from the receiver and continue its work, expecting a reply from the receiver with the answer at some future point.
In a complex microservices system, queries often need to join data in ways not supported by the initial domain model. This is where Command Query Responsibility Segregation (CQRS) comes into play; by separating the read and write models of a system.
CQRS decouples the microservice writing an event from readers that might be performing some action in response to the event. This increases the reliability, stability, and availability of the system. The read and write sides can then be scaled independently, taking best advantage of the available resources.
A decoupled design like this (using event sourcing and CQRS) makes for a lot of flexibility, gives us more headroom and knobs to turn, and allows us to better optimize the hardware efficiency, putting the money where it matters most.