From 0ff9df78da3b81d4dfc3fe4db97cf72f199a5b4b Mon Sep 17 00:00:00 2001 From: jonas lagoni Date: Wed, 16 Oct 2024 15:20:15 +0200 Subject: [PATCH 1/3] add intial request and reply --- patterns/request-and-reply.md | 181 ++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 patterns/request-and-reply.md diff --git a/patterns/request-and-reply.md b/patterns/request-and-reply.md new file mode 100644 index 0000000..925ead3 --- /dev/null +++ b/patterns/request-and-reply.md @@ -0,0 +1,181 @@ +--- +title: Request and Reply +description: +--- + +# Request and Reply + +Request and Reply is one of the most used messaging pattern and offers a two-way communication pattern to exchange messages. The most known form being REST and HTTP where you make synchronous requests over a single channel/path. + +In the event driven world it can quickly become complex as often there are no single channel and can be more dynamic. + +In request and reply there are always two parties, the requester, making the request and the replier, in charge of delivering the response to the request according to how it's expected. + + + + + + + + + + + + +
Requester Replier
+ +```diff +{ + "asyncapi": "3.0.0", + "info": { + "title": "Payment service", + "description": "The requester", + "version": "1.0.0" + }, + "channels": { + "StartShipping": { + "address": "shipping/start", + "messages": { + "StartShipping": { + "$ref": "#/components/messages/StartShipping" + }, + "StartShippingReply": { + "$ref": "#/components/messages/StartShippingReply" + } + } + } + }, + "operations": { + "StartShippingRequest": { ++ "action": "send", + "channel": { + "$ref": "#/channels/StartShipping" + }, + "messages": [ + { + "$ref": "#/channels/ping/messages/StartShipping" + } + ], + "reply": { + "channel": { + "$ref": "#/channels/StartShipping" + }, + "messages": [ + { + "$ref": "#/channels/ping/messages/StartShippingReply" + } + ] + } + } + }, + ... +} +``` + + +```diff +{ + "asyncapi": "3.0.0", + "info": { + "title": "Shipping service", + "description": "The replier", + "version": "1.0.0" + }, + "channels": { + "StartShipping": { + ... + } + }, + "operations": { + "StartShippingResponse": { ++ "action": "receive", + "channel": { + "$ref": "#/channels/StartShipping" + }, + "messages": [ + { + "$ref": "#/channels/ping/messages/StartShipping" + } + ], + "reply": { + "channel": { + "$ref": "#/channels/StartShipping" + }, + "messages": [ + { + "$ref": "#/channels/ping/messages/StartShippingReply" + } + ] + } + } + }, + ... +} +``` + +
+ +As you can see both take leverage on the same definitions where only the `receive` and `send` action keywords change. You could also use traits for operations and references to simplify the definitions. + +## Patterns + +Through request and reply there are multiple patterns that are slightly different from the simple request and reply we see in HTTP. + +### Dynamic response based on request meta information +One pattern is where the response needs to happen dynamically based on meta information in the request. + +Locations for this could be part of the headers, message payload or parameter. + +```diff +{ + "asyncapi": "3.0.0", + "info": { + "title": "Payment service", + "version": "1.0.0", + "description": "Example with a requester that initiates the request/reply pattern where the reply will happen on whatever is defined in the header `replyTo` of the request." + }, + "channels": { + "StartShipping": { + "address": "shipping/start", + "messages": { + "StartShipping": { + "$ref": "#/components/messages/StartShipping" + }, + } + }, + "StartShippingResponse": { + "address": null, + "messages": { + "StartShippingReply": { + "$ref": "#/components/messages/StartShippingReply" + } + } + } + }, + "operations": { + "StartShippingRequest": { + "action": "send", + "channel": { + "$ref": "#/channels/StartShipping" + }, + "reply": { ++ "address": { ++ "description": "The reply address is dynamically determined based on the request header `replyTo`", ++ "location": "$message.header#/replyTo" + }, + "channel": { + "$ref": "#/channels/StartShippingResponse" + } + } + } + }, + ... +} +``` + + +# Resources + +- https://eventstack.tech/posts/asyncapi-v3-request-reply +- https://www.enterpriseintegrationpatterns.com/patterns/messaging/RequestReply.html +- https://developer.ibm.com/articles/awb-request-response-messaging-pattern-introduction/ \ No newline at end of file From de7321d98367611fd41de33bd66e1a7d5fb1af61 Mon Sep 17 00:00:00 2001 From: jonas lagoni Date: Wed, 16 Oct 2024 15:21:33 +0200 Subject: [PATCH 2/3] add intial request and reply --- patterns/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/index.md b/patterns/index.md index e104a63..f8c0c86 100644 --- a/patterns/index.md +++ b/patterns/index.md @@ -21,7 +21,7 @@ Simple roadmap for patterns to document; Communication Patterns -- [ ] Request-Reply +- [X] [Request-Reply](./request-and-reply.md) - [ ] point-to-point - [ ] event streaming - [ ] Publish-Subscribe pattern From 7913e181f92dff95020a56b7a4ab2cf55636b3a2 Mon Sep 17 00:00:00 2001 From: jonas lagoni Date: Thu, 24 Oct 2024 10:52:15 +0200 Subject: [PATCH 3/3] add more examples --- patterns/request-and-reply.md | 205 +++++++++++++++++++++++++++++----- 1 file changed, 177 insertions(+), 28 deletions(-) diff --git a/patterns/request-and-reply.md b/patterns/request-and-reply.md index 925ead3..2dc1f5f 100644 --- a/patterns/request-and-reply.md +++ b/patterns/request-and-reply.md @@ -20,7 +20,7 @@ In request and reply there are always two parties, the requester, making the req -```diff +```json { "asyncapi": "3.0.0", "info": { @@ -32,24 +32,24 @@ In request and reply there are always two parties, the requester, making the req "StartShipping": { "address": "shipping/start", "messages": { - "StartShipping": { - "$ref": "#/components/messages/StartShipping" + "StartShippingRequest": { + "$ref": "#/components/messages/StartShippingRequest" }, - "StartShippingReply": { - "$ref": "#/components/messages/StartShippingReply" + "StartShippingResponse": { + "$ref": "#/components/messages/StartShippingResponse" } } } }, "operations": { "StartShippingRequest": { -+ "action": "send", + "action": "send", "channel": { "$ref": "#/channels/StartShipping" }, "messages": [ { - "$ref": "#/channels/ping/messages/StartShipping" + "$ref": "#/channels/ping/messages/StartShippingRequest" } ], "reply": { @@ -58,7 +58,7 @@ In request and reply there are always two parties, the requester, making the req }, "messages": [ { - "$ref": "#/channels/ping/messages/StartShippingReply" + "$ref": "#/channels/ping/messages/StartShippingResponse" } ] } @@ -71,7 +71,7 @@ In request and reply there are always two parties, the requester, making the req -```diff +```json { "asyncapi": "3.0.0", "info": { @@ -85,14 +85,14 @@ In request and reply there are always two parties, the requester, making the req } }, "operations": { - "StartShippingResponse": { -+ "action": "receive", + "StartShippingReply": { + "action": "receive", "channel": { "$ref": "#/channels/StartShipping" }, "messages": [ { - "$ref": "#/channels/ping/messages/StartShipping" + "$ref": "#/channels/ping/messages/StartShippingRequest" } ], "reply": { @@ -101,7 +101,7 @@ In request and reply there are always two parties, the requester, making the req }, "messages": [ { - "$ref": "#/channels/ping/messages/StartShippingReply" + "$ref": "#/channels/ping/messages/StartShippingResponse" } ] } @@ -126,28 +126,35 @@ One pattern is where the response needs to happen dynamically based on meta info Locations for this could be part of the headers, message payload or parameter. -```diff + + + + + + + + + +
Payload Headers
+ +```json { "asyncapi": "3.0.0", "info": { "title": "Payment service", - "version": "1.0.0", - "description": "Example with a requester that initiates the request/reply pattern where the reply will happen on whatever is defined in the header `replyTo` of the request." + "version": "1.0.0" }, "channels": { - "StartShipping": { + "StartShippingRequest": { "address": "shipping/start", "messages": { - "StartShipping": { - "$ref": "#/components/messages/StartShipping" + "StartShippingRequest": { + "$ref": "#/components/messages/StartShippingRequest" }, } }, - "StartShippingResponse": { + "StartShippingReply": { "address": null, "messages": { - "StartShippingReply": { - "$ref": "#/components/messages/StartShippingReply" + "StartShippingResponse": { + "$ref": "#/components/messages/StartShippingResponse" } } } @@ -156,23 +163,165 @@ Locations for this could be part of the headers, message payload or parameter. "StartShippingRequest": { "action": "send", "channel": { - "$ref": "#/channels/StartShipping" + "$ref": "#/channels/StartShippingRequest" }, "reply": { -+ "address": { -+ "description": "The reply address is dynamically determined based on the request header `replyTo`", -+ "location": "$message.header#/replyTo" - }, + "address": { + "description": "The reply address is dynamically determined + based on the request header `replyTo`", + "location": "$message.payload#/replyTo" + }, "channel": { - "$ref": "#/channels/StartShippingResponse" + "$ref": "#/channels/StartShippingReply" } } } }, + "components": { + "messages": { + "StartShippingRequest": { + "payload": { + "type": "object", + "properties": { + "replyTo": { + "type": "string" + } + } + } + } + } + } ... } ``` + + +```json +{ + "asyncapi": "3.0.0", + "info": { + "title": "Payment service", + "version": "1.0.0" + }, + "channels": { + "StartShippingRequest": { + "address": "shipping/start", + "messages": { + "StartShippingRequest": { + "$ref": "#/components/messages/StartShippingRequest" + }, + } + }, + "StartShippingReply": { + "address": null, + "messages": { + "StartShippingResponse": { + "$ref": "#/components/messages/StartShippingResponse" + } + } + } + }, + "operations": { + "StartShippingRequest": { + "action": "send", + "channel": { + "$ref": "#/channels/StartShippingRequest" + }, + "reply": { + "address": { + "description": "The reply address is dynamically determined + based on the request header `replyTo`", + "location": "$message.header#/replyTo" + }, + "channel": { + "$ref": "#/channels/StartShippingReply" + } + } + } + }, + "components": { + "messages": { + "StartShippingRequest": { + "headers": { + "type": "object", + "properties": { + "replyTo": { + "type": "string" + } + } + } + } + } + } +} +``` +
+ + +### Multiple messages over the same channel +This pattern is a common one in WebSocket, where multiple different messages flow through the same connection, + + + + + + + + +
WebSocket
+ +> Simulating a request and reply scenario, where we expect a specific message results in another over the same connection. + +```json +{ + "asyncapi": "3.0.0", + "info": { + "title": "WebSocket request/reply example", + "version": "1.0.0" + }, + "channels": { + "rootChannel": { + "address": "/", + "messages": { + "StartShippingRequest": { + "$ref": "#/components/messages/StartShippingRequest" + }, + "StartShippingResponse": { + "$ref": "#/components/messages/StartShippingResponse" + } + } + } + }, + "operations": { + "StartShippingRequest": { + "action": "send", + "channel": { + "$ref": "#/channels/rootChannel" + }, + "messages": [ + { + "$ref": "/components/messages/StartShippingRequest" + } + ], + "reply": { + "messages": [ + { + "$ref": "/components/messages/StartShippingResponse" + } + ], + "channel": { + "$ref": "#/channels/rootChannel" + } + } + } + } +} +``` +
# Resources