SOAP Gateway Answer Hub
Expose one public SOAP API over independent answer providers, forwarding matching operations to provider SOAP APIs automatically and routing one explicitly through broker request-response.
Examplesoapwsdlgatewayapi-facadeapi-compositionapibrokerrequest-responseuidashboard
🌅 Horizon
Example at a Glance
Overview
Clients should not need to know which service answers which question. This example gives them one public SOAP Answer API, with a single WSDL, for the current time, date, day, day tip, and Amsterdam zone time. A gateway service owns that boundary: it forwards most operations to the provider SOAP APIs that already offer them, and routes the one operation without a provider API through a broker request-response topic.
It is the SOAP counterpart of gateway-answer-hub. Only the
API style changes: every API is declared with style:soap,
and the services, broker, UI, and deployment stay the same.
Architecture
The UI calls only the gateway SOAP endpoint. Four operations are forwarded automatically to included APIs; zone time is routed explicitly to a broker responder.
What It Demonstrates
@api—style:soapAPIs with their own namespace, endpoint path, and SOAP version, each generating a SOAP server, typed client, and WSDL.@api—includecomposes two provider SOAP APIs into one public gateway SOAP API that adds its own operation.@service—impl gateway api ... includingforwards matching operations automatically, and an explicitconnectroutes the rest.@broker— a request-response topic answered by a service that exposes no API.@expression— small provider expressions that produce each answer.@dashboardand@ui— a generated interface whose buttons call only the gateway API.@configand@deploy— shared service configuration and deployment of the gateway, providers, broker, and UI.
Expected Result
Each dashboard button calls one SOAP 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.
🧭 Voyage
1. Problem and Constraints
A public API should be declared once and routed without repeating a
connect for every operation that a provider already
implements. Operations that no provider API offers must still be
routable, and an explicit route must be visible in the model. All APIs
use SOAP, so the public contract is one WSDL even though the operations
come from several services. SOAP faults, explicit actions, SOAP headers,
and WS-Security are out of scope, as are gateway policies such as
authentication and rate limiting.
2. Prerequisites
- Ocean UI with the gateway capability of
@service, API composition in@api, and the SOAP API style. - A container runtime for the generated deployment.
- Recommended:
gateway-answer-hub, the same example with REST APIs. - Optional: a SOAP client such as SoapUI, or
curl, for calling the endpoints directly.
3. Example Structure
0016-soap-gateway-answer-hub/
├── 00-sgah-info.ocn
├── 10-sgah-api.ocn
├── 20-sgah-broker.ocn
├── 30-sgah-config.ocn
├── 40-sgah-expression.ocn
├── 50-sgah-service-calendar.ocn
├── 50-sgah-service-gateway.ocn
├── 50-sgah-service-time-date.ocn
├── 50-sgah-service-zone-time.ocn
├── 55-sgah-dashboard.ocn
├── 56-sgah-ui.ocn
├── 60-sgah-deploy.ocn
├── architecture.mmd
├── broker-communication.mmd
└── example-info/example-info.html
4. Declare and Compose the SOAP APIs
10-sgah-api.ocn defines the two provider APIs and the public
AnswerGatewayApi, all with style:soap. Each API
declares its own XML namespace, endpointPath,
and soapVersion; Ocean generates a SOAP server, typed
client, and WSDL for each one.
TimeDateApi style:soap
engine = soap
configType = ApiConfig
version = 1.0.0
namespace = https://ocean-lab.ai/examples/answer-hub/time-date/v1
endpointPath = /time-date
soapVersion = 1.2
operation getTime() : String
operation getDate() : String
include reuses the provider operations without repeating
them. The gateway API owns the transport settings, so the included
operations are exposed through its namespace, /answers
endpoint path, SOAP version, and WSDL.
AnswerGatewayApi style:soap
engine = soap
configType = ApiConfig
version = 1.0.0
namespace = https://ocean-lab.ai/examples/answer-hub/answers/v1
endpointPath = /answers
soapVersion = 1.2
include TimeDateApi, CalendarApi
operation getZoneTime() : String
The effective public API is one SOAP endpoint, /answers,
with the operations getTime, getDate,
getDay, getDayTip, and
getZoneTime. No operation declares action, so
each SOAP action is derived as <namespace>/<OperationName>,
for example https://ocean-lab.ai/examples/answer-hub/answers/v1/getTime.
5. Implement the Gateway Service
50-sgah-service-gateway.ocn implements the public SOAP API as a
gateway. Every name after including is a local
use api alias. getTime, getDate,
getDay, and getDayTip match included operations
and are forwarded automatically from the gateway's SOAP endpoint to the
provider's SOAP endpoint. getZoneTime has no
included counterpart, so it is connected explicitly to the broker topic.
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
An explicit connect on a gateway operation also takes
precedence over automatic forwarding, so the same form can override a
forwarded route.
6. Provide the Answers
TimeDateService and CalendarService implement
their provider SOAP APIs with ordinary impl api and
connect statements to expressions; nothing in a service
changes because an API uses SOAP instead of REST.
ZoneTimeService implements no API: it answers the
request-response topic declared in 20-sgah-broker.ocn.
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
GetAmsterdamTime calls
GetZoneTimeString("Europe/Amsterdam").
7. Call Only the Gateway from the UI
AnswerHubUi uses AnswerGatewayApi, through its
generated SOAP client, and nothing else. Each button on AnswerHubDashboard calls one public
operation, so the UI does not depend on where each answer is produced.
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 soap engine, the broker uses
nats, and the UI is generated with HTMX, Bootstrap, and Go
templates. 60-sgah-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 SOAP 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
SOAP Gateway Answer Hubexample in Ocean UI. - Validate and generate the complete model, including the WSDL of each SOAP API.
- 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.
- Use the Amsterdam zone time button; the answer is returned by
ZoneTimeServicethrough the broker. - Open the generated WSDL of
AnswerGatewayApiand confirm that it lists all five operations under thehttps://ocean-lab.ai/examples/answer-hub/answers/v1namespace, although onlygetZoneTimeis declared locally. - Call
getTimedirectly onlocalhost:9100/answersand compare it withgetTimeonlocalhost:9101/time-date. Adjust the body element if it differs from the generated WSDL:curl -X POST localhost:9100/answers \ -H 'Content-Type: application/soap+xml; charset=utf-8; action="https://ocean-lab.ai/examples/answer-hub/answers/v1/getTime"' \ -d '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Body><getTime xmlns="https://ocean-lab.ai/examples/answer-hub/answers/v1"/></soap:Body> </soap:Envelope>' - Stop
ZoneTimeServiceand callgetZoneTime; the call returns a SOAP fault after its two-second timeout while the forwarded operations still answer.
11. Experiments
- Add
connect gw.getTime -> broker.answers.zoneTimeand observe that the explicit route overrides automatic forwarding. - Remove
calendarfromincludingand validate;getDayandgetDayTipbecome unresolved public operations. - Remove the
9101and9102exports so that the gateway is the only reachable API. - Change
AnswerGatewayApitosoapVersion = 1.1; clients now call the gateway with SOAP 1.1 while the providers stay on SOAP 1.2. - Change
CalendarApitostyle:restand validate; the include is rejected because every included API must use the same style as the composing API.
Executable model
<\> Implementation
Explore the runnable model by responsibility, then select a file to inspect its complete source.
# @ocean-meta-start
# tags:
# - soap
# - gateway
# - api-facade
# - broker-request-response
# perspective:
# feature: soap-gateway-answer-hub
# boundedContext: answer-hub
# @ocean-meta-end
@info
name: SOAP Gateway Answer Hub
version: 1.0.0
title: SOAP Gateway Answer Hub
subtitle: One public SOAP API for independent answer providers
shortDescription: A focused SOAP API gateway example with WSDL-backed API composition, automatic forwarding, and an explicit broker-backed operation.
description:
Overview
SOAP Gateway Answer Hub exposes one public SOAP API for time, date, day, day tip, and zone time. It is the SOAP counterpart of Gateway Answer Hub: the same gateway and routing model, with every API declared as style:soap instead of REST.
SOAP contracts
Each API declares its own XML namespace, endpoint path, and SOAP version. Ocean generates a SOAP server, typed client, and WSDL contract for every API from the same definition.
Automatic forwarding
The gateway includes the TimeDateApi and CalendarApi. Their operations are exposed through the gateway's own namespace, endpoint path, SOAP version, and WSDL. Operations with the same name and compatible contracts are forwarded automatically to their owning services.
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. Each dashboard action independently invokes one public SOAP operation, so users see the gateway as the sole public boundary.
# @ocean-meta-start
# tags:
# - soap-api
# - gateway-contract
# perspective:
# feature: soap-gateway-answer-hub
# boundedContext: answer-hub
# @ocean-meta-end
@api
TimeDateApi style:soap
engine = soap
configType = ApiConfig
version = 1.0.0
namespace = https://ocean-lab.ai/examples/answer-hub/time-date/v1
endpointPath = /time-date
soapVersion = 1.2
operation getTime() : String
operation getDate() : String
CalendarApi style:soap
engine = soap
configType = ApiConfig
version = 1.0.0
namespace = https://ocean-lab.ai/examples/answer-hub/calendar/v1
endpointPath = /calendar
soapVersion = 1.2
operation getDay() : String
operation getDayTip() : String
AnswerGatewayApi style:soap
engine = soap
configType = ApiConfig
version = 1.0.0
namespace = https://ocean-lab.ai/examples/answer-hub/answers/v1
endpointPath = /answers
soapVersion = 1.2
include TimeDateApi, CalendarApi
operation getZoneTime() : String
# @ocean-meta-start
# tags:
# - broker
# - nats
# - request-response
# perspective:
# feature: soap-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: soap-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: soap-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: soap-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
# - soap
# perspective:
# feature: soap-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
# from the gateway's SOAP endpoint to the providers' SOAP endpoints.
# Zone time is intentionally broker-backed and therefore routed explicitly.
connect gw.getZoneTime -> broker.answers.zoneTime
# @ocean-meta-start
# tags:
# - service
# - provider
# perspective:
# feature: soap-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: soap-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: soap-gateway-answer-hub
# boundedContext: answer-hub
# service: answer-hub-ui
# @ocean-meta-end
@dashboard
AnswerHubDashboard
title: SOAP Gateway Answer Hub
layout: SingleColumnLayout
Widget GatewayGuide of type Text
title = One public SOAP API, three provider services
subtitle = SOAP forwarding for time, date, day, and day tip; broker request-response for zone time
content = <p>Each action calls a SOAP operation of <b>AnswerGatewayApi</b>. The gateway automatically forwards matching operations to the provider SOAP APIs and explicitly delegates zone time to a broker responder.</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 = SOAP gateway routing patterns
subtitle = Automatic forwarding and explicit routing in one service
content = <p>This example demonstrates an Ocean service that implements a SOAP gateway API. Matching operations are forwarded to included SOAP APIs automatically. A distinct operation is explicitly connected to a broker request-response topic.</p>
# @ocean-meta-start
# tags:
# - ui
# - gateway
# perspective:
# feature: soap-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 = SOAP Gateway Answer Hub
header subtitle = One public SOAP API for independent answer providers
header align = center
footer title = Built with Ocean-lab
footer subtitle = SOAP gateway feature example
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: soap-gateway-answer-hub
# boundedContext: answer-hub
# service: shared
# @ocean-meta-end
@deploy
Name: SoapGatewayAnswerHubDeploy
@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] -->|SOAP: AnswerGatewayApi| GW[AnswerGatewayService]
GW -->|SOAP forward: getTime, getDate| TD[TimeDateService]
GW -->|SOAP forward: 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: SOAP getZoneTime (POST /answers)
GW->>B: request answers.zoneTime
B->>Z: deliver request
Z-->>B: timezone-aware time
B-->>GW: reply
GW-->>UI: SOAP response: zone time