XML Gateway Answer Hub
Expose the same gateway over independent answer providers as Gateway Answer Hub, but with every operation declaring @response:xml so it answers in XML instead of JSON.
Examplexmlpayload-representationrest-apigatewayapi-facadeapi-compositionapibrokerrequest-responseuidashboard
๐ Horizon
Example at a Glance
Overview
This example gives clients one public Answer API for the current
time, date, day, day tip, and Amsterdam zone time โ the same gateway
as gateway-answer-hub โ but every operation declares
@response:xml, so Ocean generates handlers and clients
that answer in XML instead of JSON.
It is the XML counterpart of gateway-answer-hub: only the
payload representation changes, operation by operation. The API style
stays REST/gin, unlike soap-gateway-answer-hub,
where the API style itself is SOAP and XML comes from its envelope.
Architecture
The UI calls only the gateway and decodes XML responses through its generated client. Four operations are forwarded automatically to included APIs; zone time is routed explicitly to a broker responder.
What It Demonstrates
@apiโ the@response:xmlpayload representation attribute on a REST operation, generating an XML-serializing handler and client instead of the JSON default.@apiโincludecomposes two provider APIs into one public gateway API; each included operation keeps its own declared representation.@serviceโimpl gateway api ... includingforwards matching operations automatically, and an explicitconnectroutes the rest, independent of payload representation.@brokerโ a request-response topic answered by a service that exposes no API; the broker hop is unaffected by the gateway's own REST representation.@expressionโ small provider expressions that produce each answer.@dashboardand@uiโ a generated interface whose buttons call only the gateway API and transparently decode its XML responses.@configand@deployโ shared service configuration and deployment of the gateway, providers, broker, and UI.
Expected Result
Each dashboard button calls one operation of the public gateway API and displays its answer. Time, date, day, and day tip come from their provider services through automatic forwarding; zone time comes from the broker responder through the explicit route. Every response on the wire is XML.
๐งญ Voyage
1. Problem and Constraints
Some clients or contracts require XML instead of JSON, but that choice
should not force a different API style, a different gateway, or a
different routing model. Payload representation must be declarable
per operation, defaulted sensibly, and preserved through composition
and forwarding. Gateway policies such as authentication, CORS, and
rate limiting are out of scope here, exactly as in
gateway-answer-hub.
2. Prerequisites
- Ocean UI with the gateway capability of
@service, API composition in@api, and REST payload representation attributes. - A container runtime for the generated deployment.
- Recommended:
gateway-answer-hub, the same example answering in JSON. - Optional:
soap-gateway-answer-hub, for contrast with a SOAP API style whose envelope is always XML.
3. Example Structure
0019-xml-gateway-answer-hub/
โโโ 00-xgah-info.ocn
โโโ 10-xgah-api.ocn
โโโ 20-xgah-broker.ocn
โโโ 30-xgah-config.ocn
โโโ 40-xgah-expression.ocn
โโโ 50-xgah-service-calendar.ocn
โโโ 50-xgah-service-gateway.ocn
โโโ 50-xgah-service-time-date.ocn
โโโ 50-xgah-service-zone-time.ocn
โโโ 55-xgah-dashboard.ocn
โโโ 56-xgah-ui.ocn
โโโ 60-xgah-deploy.ocn
โโโ architecture.mmd
โโโ broker-communication.mmd
โโโ example-info/example-info.html
4. Declare the XML Response Representation
10-xgah-api.ocn is 10-gah-api.ocn with
@response:xml added to every operation. No operation
declares @request: every operation is a bodyless
GET, and GET/DELETE cannot
declare a request representation at all.
TimeDateApi style:rest
engine = gin
configType = ApiConfig
version = 1.0.0
basePath = /time-date
generateSwagger = true
get /time getTime() : String @response:xml
get /date getDate() : String @response:xml
include still reuses the provider operations without
repeating them; each included operation keeps the representation it
was declared with.
AnswerGatewayApi style:rest
engine = gin
configType = ApiConfig
version = 1.0.0
basePath = /answers
generateSwagger = true
include TimeDateApi, CalendarApi
get /zone-time getZoneTime() : String @response:xml
The generated handler for each operation writes its result with
c.XML(http.StatusOK, result) instead of
c.JSON(...), and its Swagger annotation declares
@Produce application/xml.
5. Implement the Gateway Service
50-xgah-service-gateway.ocn is unchanged from
gateway-answer-hub: forwarding and explicit routing do
not depend on payload representation.
use api TimeDateApi as timeDate
use api CalendarApi as calendar
use broker AnswerHubBroker as broker
impl gateway api AnswerGatewayApi as gw on gwCfg.apiConfig.port
including timeDate, calendar
connect gw.getZoneTime -> broker.answers.zoneTime
6. Provide the Answers
TimeDateService and CalendarService
implement their provider APIs with ordinary connect
statements to expressions; nothing changes here because the
representation is declared on the API, not the service.
ZoneTimeService implements no API: it answers the
request-response topic declared in 20-xgah-broker.ocn,
which is untouched by @response:xml.
AnswerHubBroker
engine = nats
configType = AnswerHubBrokerConfig
answers.zoneTime : _-String as request-response with timeout:2s
ZoneTimeService
use broker AnswerHubBroker as broker
use expression GetAmsterdamTime as zoneTime
connect broker.answers.zoneTime -> zoneTime
7. Call Only the Gateway from the UI
AnswerHubUi uses AnswerGatewayApi and
nothing else. Its generated backend client decodes the XML response
of each call transparently, so the dashboard wiring is identical to
the JSON example.
use api AnswerGatewayApi as gateway
connect answers.TimeButton.click -> gateway.getTime
connect answers.ZoneTimeButton.click -> gateway.getZoneTime
8. Select Technologies and Deploy
The APIs use the gin REST engine, the broker uses
nats, and the UI is generated with HTMX, Bootstrap, and Go
templates. 60-xgah-deploy.ocn imports a NATS container and
starts the gateway after its providers and the broker.
GatewayDeploy
service AnswerGatewayService
replica 1
export 9100:AnswerGatewayService.gw
dependsOn TimeDateDeploy, CalendarDeploy, ZoneTimeDeploy, Broker
AnswerHubUiDeploy
service AnswerHubUi
replica 1
export 8085:AnswerHubUi.config
dependsOn GatewayDeploy
The provider APIs are also exported on ports 9101 and
9102 for direct inspection. The UI does not use them.
9. Validate, Generate, and Run
- Open the
XML Gateway Answer Hubexample in Ocean UI. - Validate and generate the complete model.
- Follow the generated
Readme.mdto start the broker, services, and UI. - Open the UI at
localhost:8085.
10. Verify the Result
- Use the time, date, day, and day tip buttons; each answer is forwarded by the gateway to its provider service and rendered from an XML response.
- Use the Amsterdam zone time button; the answer is returned by
ZoneTimeServicethrough the broker and still rendered as XML by the gateway. - Call
GET localhost:9100/answers/timedirectly and compare the response body andContent-TypewithGET localhost:9101/time-date/time; both areapplication/xmland look like<string>the current time</string>. - Stop
ZoneTimeServiceand call/answers/zone-time; the request fails after its two-second timeout while the forwarded operations still answer.
11. Experiments
- Remove
@response:xmlfromgetZoneTimeonly, and validate; the gateway now answersgetZoneTimein JSON while the other four operations stay XML, since representation is per-operation. - Add
connect gw.getTime -> broker.answers.zoneTimeand observe that the explicit route overrides automatic forwarding, unrelated to representation. - Remove
calendarfromincludingand validate;getDayandgetDayTipbecome unresolved public operations. - Compare this directory with
gateway-answer-hubusing a diff; the only substantive change is the five@response:xmlannotations in10-xgah-api.ocn.
Executable model
<\> Implementation
Explore the runnable model by responsibility, then select a file to inspect its complete source.
# @ocean-meta-start
# tags:
# - gateway
# - api-facade
# - broker-request-response
# - xml
# - payload-representation
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# @ocean-meta-end
@info
name: XML Gateway Answer Hub
version: 1.0.0
title: XML Gateway Answer Hub
subtitle: One public API for independent answer providers, answering in XML
shortDescription: The REST/JSON gateway example with every operation declaring @response:xml instead of the JSON default.
description:
Overview
XML Gateway Answer Hub is the REST/XML counterpart of Gateway Answer Hub. It exposes the same one public API for time, date, day, day tip, and zone time, but every operation declares @response:xml so Ocean generates XML-serializing handlers and clients instead of the JSON default.
Payload representation
Each operation in 10-xgah-api.ocn adds @response:xml. Nothing about the gateway, forwarding, or broker routing model changes: it demonstrates that payload representation is an orthogonal, per-operation concern from routing.
Automatic forwarding
The gateway includes the TimeDateApi and CalendarApi. Operations with the same name and compatible contracts are forwarded automatically to their owning services, XML representation included.
Explicit routing
Zone time is intentionally not exposed by an internal API. The gateway routes getZoneTime explicitly through a NATS request-response topic to ZoneTimeService.
UI
AnswerHubUi calls only AnswerGatewayApi. Its generated backend client decodes each XML response transparently, so the dashboard code is unaware of the representation.
# @ocean-meta-start
# tags:
# - rest-api
# - gateway-contract
# - xml
# - payload-representation
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# @ocean-meta-end
@api
TimeDateApi style:rest
engine = gin
configType = ApiConfig
version = 1.0.0
basePath = /time-date
generateSwagger = true
get /time getTime() : String @response:xml
get /date getDate() : String @response:xml
CalendarApi style:rest
engine = gin
configType = ApiConfig
version = 1.0.0
basePath = /calendar
generateSwagger = true
get /day getDay() : String @response:xml
get /day-tip getDayTip() : String @response:xml
AnswerGatewayApi style:rest
engine = gin
configType = ApiConfig
version = 1.0.0
basePath = /answers
generateSwagger = true
include TimeDateApi, CalendarApi
get /zone-time getZoneTime() : String @response:xml
# @ocean-meta-start
# tags:
# - broker
# - nats
# - request-response
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# @ocean-meta-end
@broker
AnswerHubBroker
@perspectives: version:1.0.0, lifestyle:stable
engine = nats
configType = AnswerHubBrokerConfig
tags = primary, shared
answers.zoneTime : _-String as request-response with timeout:2s
# @ocean-meta-start
# tags:
# - configuration
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: shared
# @ocean-meta-end
@config
@import config O.broker.nats.config@1.0.0 as BrokerConfig
@import config O.log.config@1.0.0 as LogConfig
ApiConfig
port : Int (default=8080)
CommonServiceConfig
apiConfig : ApiConfig
brokerConfig : BrokerConfig
logConfig : LogConfig
AnswerHubBrokerConfig
brokerConfig : BrokerConfig
logConfig : LogConfig
UiConfig
port : Int (default=8080)
logConfig : LogConfig
# @ocean-meta-start
# tags:
# - expression
# - answer-provider
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: shared
# @ocean-meta-end
@expression
GetTimeString
input: _
output: result:String
logic
result = nowTimeString()
GetDateString
input: _
output: result:String
logic
result = nowDateString()
GetDayString
input: _
output: result:String
logic
result = today()
GetDayTip
input: _
output: result:String
logic
result = tipOfTheDay()
GetAmsterdamTime
input: _
output: result:String
logic
result = GetZoneTimeString("Europe/Amsterdam")
# @ocean-meta-start
# tags:
# - service
# - provider
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: calendar-service
# @ocean-meta-end
@service
CalendarService
@perspectives: version:1.0.0, lifestyle:stable
use config CommonServiceConfig as svcCfg
impl api CalendarApi as api on svcCfg.apiConfig.port
use expression GetDayString as day
use expression GetDayTip as tip
connect api.getDay -> day
connect api.getDayTip -> tip
# @ocean-meta-start
# tags:
# - service
# - gateway
# - api-facade
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: answer-gateway-service
# @ocean-meta-end
@service
AnswerGatewayService
@perspectives: version:1.0.0, lifestyle:stable
use config CommonServiceConfig as gwCfg
use api TimeDateApi as timeDate
use api CalendarApi as calendar
use broker AnswerHubBroker as broker
impl gateway api AnswerGatewayApi as gw on gwCfg.apiConfig.port
including timeDate, calendar
# getTime, getDate, getDay, and getDayTip are automatically forwarded.
# Zone time is intentionally broker-backed and therefore routed explicitly.
connect gw.getZoneTime -> broker.answers.zoneTime
# @ocean-meta-start
# tags:
# - service
# - provider
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: time-date-service
# @ocean-meta-end
@service
TimeDateService
@perspectives: version:1.0.0, lifestyle:stable
use config CommonServiceConfig as svcCfg
impl api TimeDateApi as api on svcCfg.apiConfig.port
use expression GetTimeString as time
use expression GetDateString as date
connect api.getTime -> time
connect api.getDate -> date
# @ocean-meta-start
# tags:
# - service
# - broker-responder
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: zone-time-service
# @ocean-meta-end
@service
ZoneTimeService
@perspectives: version:1.0.0, lifestyle:stable
use config CommonServiceConfig as svcCfg
use broker AnswerHubBroker as broker
use expression GetAmsterdamTime as zoneTime
connect broker.answers.zoneTime -> zoneTime
# @ocean-meta-start
# tags:
# - dashboard
# - gateway
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: answer-hub-ui
# @ocean-meta-end
@dashboard
AnswerHubDashboard
title: XML Gateway Answer Hub
layout: SingleColumnLayout
Widget GatewayGuide of type Text
title = One public API, three provider services, XML answers
subtitle = API forwarding for time, date, day, and day tip; broker request-response for zone time
content = <p>Each action calls <b>AnswerGatewayApi</b>. Every operation declares <code>@response:xml</code>, so the gateway and its providers answer in XML instead of JSON.</p>
Widget TimeButton of type Button
label = Current time
Widget DateButton of type Button
label = Current date
Widget DayButton of type Button
label = Current day
Widget DayTipButton of type Button
label = Day tip
Widget ZoneTimeButton of type Button
label = Amsterdam zone time
AboutDashboard
title: About this example
layout: SingleColumnLayout
Widget AboutText of type Text
title = XML payload representation
subtitle = The same gateway and routing model as Gateway Answer Hub, answering in XML
content = <p>This example demonstrates the <code>@response:xml</code> payload representation attribute of <code>dsl.api</code>. Matching endpoints are still forwarded to included APIs automatically, and a distinct endpoint is still connected explicitly to a broker request-response topic; only the wire format of every response changes, from JSON to XML.</p>
# @ocean-meta-start
# tags:
# - ui
# - gateway
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: answer-hub-ui
# @ocean-meta-end
@ui
AnswerHubUi
@perspectives: version:1.0.0, lifestyle:stable
framework: htmx
styling: bootstrap
template: go-html-template
backend: go-gin
static: static
configType: UiConfig
nav Answers dashboard = AnswerHubDashboard
nav About dashboard = AboutDashboard
header title = XML Gateway Answer Hub
header subtitle = One public API for independent answer providers, answering in XML
header align = center
footer title = Built with Ocean-lab
footer subtitle = Gateway feature example, XML payload representation
footer align = center
use dashboard AnswerHubDashboard as answers
use dashboard AboutDashboard as about
use api AnswerGatewayApi as gateway
connect answers.TimeButton.click -> gateway.getTime
connect answers.DateButton.click -> gateway.getDate
connect answers.DayButton.click -> gateway.getDay
connect answers.DayTipButton.click -> gateway.getDayTip
connect answers.ZoneTimeButton.click -> gateway.getZoneTime
# @ocean-meta-start
# tags:
# - deployment
# - gateway
# perspective:
# feature: xml-gateway-answer-hub
# boundedContext: answer-hub
# service: shared
# @ocean-meta-end
@deploy
Name: XmlGatewayAnswerHubDeploy
@import service P.broker.nats.docker@1.0.0 as NATS
TimeDateDeploy
service TimeDateService
replica 1
export 9101:TimeDateService.api
CalendarDeploy
service CalendarService
replica 1
export 9102:CalendarService.api
ZoneTimeDeploy
service ZoneTimeService
replica 1
dependsOn Broker
GatewayDeploy
service AnswerGatewayService
replica 1
export 9100:AnswerGatewayService.gw
dependsOn TimeDateDeploy, CalendarDeploy, ZoneTimeDeploy, Broker
Broker
service NATS
AnswerHubUiDeploy
service AnswerHubUi
replica 1
export 8085:AnswerHubUi.config
dependsOn GatewayDeploy
flowchart LR
UI[AnswerHubUi] -->|AnswerGatewayApi XML| GW[AnswerGatewayService]
GW -->|automatic: getTime, getDate| TD[TimeDateService]
GW -->|automatic: getDay, getDayTip| CAL[CalendarService]
GW -->|request: answers.zoneTime| BROKER[(AnswerHubBroker)]
BROKER -->|reply| ZONE[ZoneTimeService]
sequenceDiagram
participant UI as AnswerHubUi
participant GW as AnswerGatewayService
participant B as AnswerHubBroker
participant Z as ZoneTimeService
UI->>GW: GET /answers/zone-time
GW->>B: request answers.zoneTime
B->>Z: deliver request
Z-->>B: timezone-aware time
B-->>GW: reply
GW-->>UI: zone time (application/xml)