Microservice Interactions: Query, Command, Event

Wahome
6 min readSep 18, 2019

--

Photo by Jassim Vailoces on Unsplash

One of the most salient principles with a Microservice Architecture is divide and conquer: the decomposition of a system into loosely coupled, discrete and isolated component services that cohesively work together by communicating over well-defined and standardized APIs to solve a larger, complex business problem.

“Without great solitude, no serious work is possible.” ~ Pablo Picasso

Isolation is at the heart of microservices and is perhaps their most important trait. An ideal microservice ought to be architected, created, deployed, maintained and retired without affecting any other microservice; in isolation. It’s a prerequisite for resilience and elasticity and requires well-defined communication boundaries between services to decouple them along the dimensions of:

  • Time: allowing concurrency
  • Space: allowing distribution and mobility

Shared-Nothing

shared-disk vs shared-nothing architectures

When talking about isolation in microservices, state is many a time the big elephant in the room. Microservices are inherently (and often) stateful entities: they encapsulate state and behavior akin to an Object or an Actor.

“Without privacy there was no point in being an individual.” ~Jonathan Franzen

Isolation most certainly applies to state: like objects and actors, a microservice should encapsulate its state and behaviour. A microservice’s database is thus effectively part of the implementation of that service and cannot be accessed directly by other services; database-per-service pattern.

A database per service is arguably the apogee of loose coupling, isolation and devolving governance through microservices. Teams specialize in and are responsible for all aspects of the software they build.

But there‘s a slight problem. That of transactions being distributed.

Distributed Transactions

Transactions that update multiple business entities are fairly common. These kinds of transactions are trivial to implement in a monolithic application because there is a single database.

When a microservice architecture decomposes a monolithic system into self-encapsulated services, it can break transactions which results in a local transaction in the monolithic system being distributed into multiple services that will be called in a sequence.

Consider the example of placing an order in an e-commerce system:

Place Order transaction in a monolithic e-commerce system

In the monolithic system depicted above, if a user makes a Place Order request, the system creates a local database transaction that wraps the operations on the Order and Inventory tables. If any step fails, the transaction can roll back hence both the order and inventory updates are reserved; a property known as ACID (Atomicity, Consistency, Isolation, Durability), which is guaranteed by the database system.

Place Order transaction in a microservice e-commerce system

The same e-commerce platform decomposed into single-function, loosely coupled microservices Order Microservice and Inventory Microservice has the implication of distributing the transaction due to each microservice owning and encapsulating its state. When a Place Order request is made by the user, both microservices will be invoked to apply changes into their own database.

Component services in a Microservice Architecture, on their own, provide no meaningful value. To derive benefits, they need to be employed as elements of a larger process — a process that provides value to an organization. For instance, in the e-commerce example above, the Order Microservice by itself is of no value to a user. The Inventory Microservice is needed to reserve an item after an order has been created in the Order Microservice thus completing a Place Order request.

But how does the Inventory Microservice get wind of what the Order Microservice has been up to? How would it know that the Order Microservice has created an order so that it can in turn play its part?

Inter-service Communication

Software legend Alan Kay, in a 2003 email exchange, rued coming up with the concept of object-oriented programming, saying:

“I’m sorry that I long ago coined the term ‘objects’ for this topic because it gets many people to focus on the lesser idea. The big idea is ‘messaging’.

The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.” ~ Alan Kay

He went on to clarify what he meant when he called Smalltalk “object-oriented”:

“OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.” ~ Alan Kay

According to Kay, the essential ingredients of OOP are thus:

  • Message passing
  • Encapsulation
  • Dynamic binding

Given the significance of inter-service communication, more emphasis needs to be placed on the arrows in architectural diagrams than on the boxes.

Interaction Patterns

“Without collaboration there is no creation”

Service interaction is considered a first-class citizen in various industry standards. The importance of interaction increases as monolithic systems are broken down into smaller services, becoming more service oriented, and as processes increasingly cross organizational boundaries.

James Lewis and Martin Fowler in their bliki about microservices had this to say about inter-process communication protocols:

“The two protocols used most commonly are HTTP request-response with resource API’s and lightweight messaging” ~ James Lewis & Martin Fowler

This utterance led to a highly charged technical argument between microservice practitioners inclined to the use of HTTP APIs and those who are inclined to the idea of “reactive microservices” an adaptation of event-driven architecture.

While healthy and with merits to both sides of the schism, the technical argument on protocols does however take away the focus on what is perhaps of greater significance: the flow of interactions — the interaction pattern which represents the practical aspects of how services and domains interact.

Inter-service interactions can be defined into three types:

1. Query

“Please tell me about…?”

A query is a on-demand request for information from another service.

In the context of the e-commerce system, an example of a query is: “How much does this item cost?”

2. Command

“Please do…?”

A command is a point-to-point message sent when a service requires an action to be carried out by another service.

A command can be declined by the receiving service as a result of:

  • malformation or structural issues
  • business rules violation
  • exception handling

In the context of the e-commerce system, an example of a command is: “I’d like to place an order for this item.”

3. Event

“…has happened”

An event is a message to numerous independent endpoints sent when an action has been carried out.

Unlike a command, an event cannot be “rejected” by the receiving service. It represents something that has already happened.

In the context of the e-commerce system, an example of a event is: “An order for this item has been placed.”

Summary

Service interaction is considered a first-class citizen in various industry standards. The importance of interaction increases as monolithic systems are broken down into smaller services, becoming more service oriented, and as processes increasingly cross organizational boundaries.

The difference between queries, commands and events lies in their intent. While queries have no special intent and are rather requests for information, commands trigger something which should happen (in the future) and events inform about something which has happened and is already completed (in the past).

Thank you for reading. I sincerely hope it was a nice read.

You can catch me at:

GitHub: kwahome

Twitter: @kwahome_

--

--

No responses yet