Pattern: Command-side replica
pattern service collaboration implementing commandsWant to learn more about this pattern?
Take a look at my self-paced, online bootcamp that teaches you how to use the Saga, API Composition, and CQRS patterns to design operations that span multiple services.
The regular price is $395/person but use coupon NPXJKULI to sign up for $95 (valid until December 25th, 2024)
Context
You have applied the Microservices architecture pattern and the Database per service pattern.
As a result, a service that implements a system command often needs to query other services.
For example, the createOrder()
command is implemented by the Order Service
, which needs to retrieve the restaurant’s menu Restaurant Service
in order to price and validate the line items.
One option is for the Order Service
to query the Restaurant Service
each time.
The query can either be implemented synchronously using, for example, or asynchronously as a saga step.
However, this approach has several drawbacks, which can be partially mitigated by caching, including more network traffic and a runtime dependency on the Restaurant Service
.
An alternative approach is to replica the restaurants’ menus to the Order Service
.
Problem
How can a service that implements a command retrieve data from another service?
There are five dark energy forces:
- Simple components - simple components consisting of few subdomains are easier to understand and maintain than complex components
- Team autonomy - a team needs to be able to develop, test and deploy their software independently of other teams
- Fast deployment pipeline - fast feedback and high deployment frequency are essential and are enabled by a fast deployment pipeline, which in turn requires components that are fast to build and test.
- Support multiple technology stacks - subdomains are sometimes implemented using a variety of technologies; and developers need to evolve the application’s technology stack, e.g. use current versions of languages and frameworks
- Segregate by characteristics - e.g. resource requirements to improve scalability, their availability requirements to improve availability, their security requirements to improve security, etc.
There are five dark matter forces:
- Simple interactions - an operation that’s local to a component or consists of a few simple interactions between components is easier to understand and troubleshoot than a distributed operation, especially one consisting of complex interactions
- Efficient interactions - a distributed operation that involves lots of network round trips and large data transfers can be too inefficient
- Prefer ACID over BASE - it’s easier to implement an operation as an ACID transaction rather than, for example, eventually consistent sagas
- Minimize runtime coupling - to maximize the availability and reduce the latency of an operation
- Minimize design time coupling - reduce the likelihood of changing services in lockstep, which reduces productivity
Solution
The solution consists of the following elements:
- Command service - the service that implements the command.
- Provider service - the service that owns the data that the command service needs
- Replica database - a read-only replica of the data from the provider service. The command service keeps the replica up to data by subscribing to Domain events published by the provider service
Resulting context
Benefits
This pattern resolves the following forces:
- Support multiple technology stacks - the replica database can use a technology stack that’s more optimized to support the command
- Segregate by characteristics - the provider service is no longer invoked by the command and so might need to be as performant or available
- Simple interactions - the command is simpler since it no longer needs to interact with the provider service
- Efficient interactions - the command is more efficient since it no longer needs to interact with the provider service
- Prefer ACID over BASE - the replica is potentially stale
- Minimize runtime coupling - runtime coupling is reduced since the the command no longer needs to interact with the provider service
Drawbacks
This pattern potentially fails to resolve the following forces:
- Simple components - makes the command service more complicated since it needs to maintain the replica. It also complicates the provider service since it must publish the events.
- Team autonomy - the provider service’s team might need to coordinate more frequently with command service team due to increased design-time coupling (see below)
- Fast deployment pipeline - the command service’s deployment pipeline might be slower since it needs test the replica
- Segregate by characteristics - potentially increased since, for example, if the replica database contains regulated data (e.g. PII) then the command service might be more complicated.
- Simple interactions - potentially more complicated since the provider service’s commands must publish events
- Efficient interactions - potentially less efficient since the provider service might publish a large volume of events
- Minimize design-time coupling - risk of tight design-time coupling between services, since the command service needs to know about the structure of the data that is replicated and potentially its lifecycle events.
Related patterns
- The Database per Service pattern creates the need for this pattern
- The Saga is an alternative solution
- The Domain event pattern generates the events
- This pattern is structurally identical to CQRS.
See also
- Eventuate, which is a platform for developing transactional business applications.
Learn more
- My book Microservices patterns describes this pattern in a lot more detail
- Take a look at my self-paced, online bootcamp that teaches you how to use the Saga, API Composition, and CQRS patterns to design operations that span multiple services.