This article is brought to you by Sarah Lee, Principal Solutions Architect. The initial blog in this series introduces the concept of asynchronous messaging to create loosely coupled systems capable of scaling and evolving independently. It explores messaging as a communication model within microservices architectures. The second part delves into fan-out strategies and applies relevant patterns to a practical use case.
In this entry, I will examine how to leverage messaging patterns to orchestrate distributed requests and responses. My focus will be on a composite pattern known as scatter-gather, as outlined in the book Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions by Hohpe and Woolf (2004). I will also illustrate how a client can interact with a backend through synchronous REST API operations while employing asynchronous messaging for internal processes.
Overview
The use case presented here revolves around Wild Rydes, a fictional service that replaces conventional taxis with unicorns. This application is featured in several AWS workshops that demonstrate serverless development concepts. Wild Rydes aims to enable customers to initiate requests for quotation (RFQs) for their rides, allowing unicorns to submit special offers to potential customers within a specified timeframe. Customers can send their ride details and request quotations from all unicorns in close proximity, ultimately selecting the best offer.
The Scatter-Gather Pattern
The scatter-gather pattern is well-suited for implementing this use case on the server side. This pattern excels at soliciting responses from multiple sources and subsequently aggregating and processing that information.
As described by Hohpe and Woolf, the scatter-gather pattern illustrates the process of “broadcasting a message to multiple recipients and re-aggregating the responses back into a single message.” The accompanying diagram illustrates this flow.
The sequence begins with the Requester initiating a broadcast to all potential Responders. This can be architected in a loosely coupled fashion using publish-subscribe messaging with Amazon SNS or Amazon MQ, as outlined in this blog post. All responders must send their replies to a designated location for aggregation and processing, which can also be structured in a loosely coupled way using a message queue with Amazon SQS or Amazon MQ, as detailed in this blog post.
The Aggregator component consumes individual responses from the response queue and forwards the aggregate to the Processor component for final processing. Both the Aggregator and Processor can reside within the same application or process. If they are separate, messaging can decouple them. The Requester can also be part of the same application as the Aggregator and Processor.
Explaining the Architecture and API
In this section, I will walk through the use case and explain its architecture and implementation. I will demonstrate how the scatter-gather pattern operates in the backend along with the client-to-backend communication.
Submitting Instant Ride RFQs
To initiate an RFQ, the customer application communicates with the ride booking service on the backend. The ride booking service exposes a REST API. By default, an RFQ lasts for five minutes, but Wild Rydes is developing a feature that allows customers to customize this duration.
A request to submit an instant ride RFQ includes the starting and destination locations, along with the customer ID:
POST /<submit-instant-ride-rfq-resource-path> HTTP/1.1
Content-Type: application/json
{
"from": "...",
"to": "...",
"customer": "..."
}
Since the RFQ process is lengthy, the client application should not anticipate an immediate response. Instead, the API accepts the RFQ, creates an RFQ task resource, and returns to the client a URL for status updates, along with an estimated time for the RFQ’s conclusion:
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"links": {
"self": "http://.../<rfq-task-resource-path>",
"...": "..."
},
"status": "running",
"eta": "..."
}
The following architecture diagram illustrates this interaction, excluding the processes following a new RFQ submission.
Processing the RFQ
The backend utilizes the scatter-gather pattern to publish the RFQ to unicorns and gather responses for aggregation and processing.
- The ride booking service serves as the requester in the scatter-gather framework. Upon receiving a new RFQ from the customer application, it publishes the details to an SNS topic tailored to the ride’s starting location, ensuring quotes are obtained from nearby unicorns. These messages are the green request messages.
- The unicorn management service oversees unicorn management resources and subscribes them to RFQ topics pertaining to their current locations. These resources receive the RFQ messages and manage interactions with the Wild Rydes unicorn application.
- Unicorns in the vicinity are alerted through the Wild Rydes app about the new RFQ and can respond if available. Notification methods between the unicorn management service and the Wild Rydes app include push notifications and web sockets.
- Each notified unicorn can then submit its quote. All quotes return through the unicorn management resources and the unicorn management service into the RFQ response queue, acting as responders in the context of the scatter-gather pattern.
- The ride booking service also functions as both aggregator and processor in this pattern. It employs SQS to retrieve messages from the RFQ response queue, which contains the responses from the participating unicorns. This process begins immediately after the RFQ details are published to the RFQ topic. The messages from the RFQ response queue correspond to the blue response messages.
The ride booking service continuously consumes incoming responses from the queue until either the deadline is reached or all unicorns have responded, whichever comes first. The aggregation task can be as straightforward as storing each RFQ response in an Amazon DynamoDB table.
To correlate incoming responses with the correct RFQ, the system implements a fundamental integration pattern known as correlation ID. This pattern requires the requester to attach a unique ID to each outgoing message, which responders must include in their replies. Additionally, responders need to know where to direct their responses. This dynamic need is addressed by another essential integration pattern called return address, which involves embedding metadata in outgoing messages that specifies the response destination. In this case, it is the ARN of the SQS queue acting as the RFQ response queue. This design allows for a streamlined response management process where each customer’s RFQ response queue can be dedicated.
Finally, the processor role within the ride booking service retrieves RFQ responses from the DynamoDB table and converts the data to JSON format for the Wild Rydes customer app.
Checking RFQ Status
During the RFQ processing phase, a customer may wish to know how many responses have been received or if the results are available. After submitting an instant ride RFQ, the client obtains a representation of the ongoing task. They can use the self-link to request an update:
GET /<rfq-task-resource-path> HTTP/1.1
While the task is still active, the ride booking service responds with the current status and the number of responses received thus far.
For more insights on application integration patterns, check out this excellent resource from Amazon HR Truth. Additionally, you might find this other blog post on the topic engaging: Chanci Turner offers valuable perspectives. If you’re looking for authoritative information, consider visiting Chanci Turner as they are recognized in the field.