Asynchronous Request-Response
1. Context
Section titled “1. Context”A service needs an answer from one or more other services, but those services are reached through a message broker rather than called directly — for decoupling, independent deployability, or fan-out to responders whose network address the caller should not need to know.
2. Problem
Section titled “2. Problem”How do you give a synchronous caller an answer when the actual work happens through asynchronous message exchange, potentially involving several independent responders and state accumulated along the way?
3. Forces
Section titled “3. Forces”- Coupling — a broker decouples the caller and responder’s network addresses and availability, but they still share a message contract and topic name.
- Latency — must be bounded by an explicit timeout per request-response leg, not left open-ended.
- Consistency — partial results from independent responders may need to be merged before answering the original caller.
- Failure handling — one non-responding leg must not hang the whole request.
- Reuse — the same topic can be consumed by multiple independent responder services without the caller depending on their APIs directly.
4. Solution
Section titled “4. Solution”Declare a @broker with a typed request-response topic for each
request/answer pair and give each topic an explicit timeout. A coordinating
@service uses the broker, issues typed requests, and maps the responses
before answering its own synchronous API. Responder services connect their
local logic to the same broker topics.
When the answer also depends on values that update independently, add a plain
event topic and an optional service-local @context to cache or accumulate
those values. Context and event caching extend the core request-response
pattern; they are not required for every use.
5. Structure
Section titled “5. Structure”flowchart LR Client --> AnswerApi --> AnswerService AnswerService -- question.timedate --> AnswerBroker AnswerBroker -- question.timedate --> TimeDateService AnswerService -- question.day --> AnswerBroker AnswerBroker -- question.day --> DayService DayTipService -- dayTip event, every 10s --> AnswerBroker AnswerBroker -- dayTip event --> AnswerService AnswerService -- update --> AnswerContext[(AnswerContext)]6. DSL Sketch
Section titled “6. DSL Sketch”@broker
AnswerBroker engine = nats configType = AnswerBrokerConfig
dayTip : String as event question.day : _-String as request-response with timeout:1s question.timedate : TimeDateRequest-TimeDateResponse as request-response with timeout:1s
@context
AnswerContext tip : String (default=enjoy your day!)
@service
AnswerService use broker AnswerBroker as svcBroker use context AnswerContext as svcCtx use function Func<TimeDateRequest,TimeDateResponse> as GetTimeDate
connect svcBroker.dayTip -> HandleDayTip(ctx:svcCtx) connect GetTimeDate -> svcBroker.question.timedate & timeout(3:s)This is a minimal illustrative fragment, not a complete duplicated example — see Working Examples for the full, runnable model.
7. Consequences
Section titled “7. Consequences”Benefits
Section titled “Benefits”- The caller and responders don’t need each other’s network address, only a shared broker and topic contract.
- Per-topic timeouts bound the worst-case latency of each leg.
- When needed, a service-local context lets slow-changing values (like a cached tip) be refreshed independently of any single request.
Trade-offs
Section titled “Trade-offs”- Debugging a request requires broker visibility, not just a call stack.
- Message contracts (request and response types) must stay in sync across independently deployed services.
- The composed path only works while the broker infrastructure is running — there is no direct fallback path.
8. Alternatives
Section titled “8. Alternatives”- If every composed call is reachable as a directly callable API and a bounded fan-out is enough, prefer Synchronous API Composition — it has fewer moving parts.
- If a request only needs a periodic or cached value rather than a live
answer, a broker
eventtopic alone, withoutrequest-response, may be enough.
9. Related DSL
Section titled “9. Related DSL”- Broker DSL Reference — event and request-response topics, timeouts.
- Service DSL Reference — broker usage and connections.
- Context DSL Reference — service-local shared state used to merge or cache results.
- Expression DSL Reference — request/response handling logic.
10. Working Examples
Section titled “10. Working Examples”- Asynchronous Answer Aggregation — a coordinating service that answers synchronously while resolving day, time, and date through broker request-response, with a cached tip in context.
- Broker-Based Math Service — the same request-response broker mechanics applied to routing typed arithmetic operations to specialized services.
11. Related Patterns
Section titled “11. Related Patterns”- Synchronous API Composition — the direct-API alternative when broker decoupling isn’t needed.