Service DSL Reference
1. Overview
Section titled “1. Overview”The @service section defines deployable Ocean units.
A service composes domain behavior and connects it to system interfaces and infrastructure. It can implement or consume APIs, host reusable integrations, use brokers and databases, orchestrate components and FSMs, use expressions and functions, bind configuration and context, expose vault-backed secrets, initialize behavior, aggregate results, and declare typed connections.
The service is the bridge between abstract domain behavior and runnable system integration.
2. Core Principle
Section titled “2. Core Principle”A service owns orchestration and deployment-facing integration—not the internal state of domain behavior.
APIs ─────────┐Integrations ─┤Brokers ──────┤Databases ────┤Config ───────┼── Service ── Components / FSMs / ExpressionsContext ──────┤Vaults ───────┤Functions ────┘Components own composition, FSMs own behavioral state, and expressions own stateless logic. The service connects these capabilities into a deployable boundary.
3. Syntax Overview
Section titled “3. Syntax Overview”@service
<ServiceName> @perspectives: <key>:<value>, ... @tags: <tag1>, <tag2>, ...
[port: <port1>, <port2>, ...]
use config <ConfigName> as <alias> use context <ContextName> as <alias> use api <ApiName> [as <alias>] impl api <ApiName> [as <alias>] on <port-or-config-path> impl gateway api <ApiName> [as <alias>] on <port-or-config-path> including <used-api-alias>, ... impl integration <IntegrationName> as <alias> use broker <BrokerName> as <alias> use database <DatabaseName> [as <alias>] use expression <ExpressionName> [as <alias>] use fsm <FSMName> as <alias> use component <ComponentName> [as <alias>] use function <FunctionType> [as <alias>] use vault <VaultName> [as <alias>]
init <expression-or-function-call> aggregate <aggregation> connect <source> -> <target>A file may define multiple services.
An optional @perspectives: line (and, less commonly, @tags:) may follow the
service name to attach comma-separated key:value metadata — for example
@perspectives: version:1.0.0, lifestyle:stable. See dsl.perspective and
dsl.tag.
4. Service Names
Section titled “4. Service Names”Service names follow the Ocean type-name convention and must be unique within the resolved service scope.
Examples:
OrderServiceInventoryServicePaymentOrchestratorA service name identifies the deployable model unit; target-specific deployment names may be derived separately.
5. Ports
Section titled “5. Ports”An implemented API selects its port with on. The port may be written inline:
impl api TodoApi as api on 8080or resolved from a configuration path — the alias of a use config binding
followed by the path to the port value:
use config TodoServiceConfig as myCfgimpl api TodoApi as api on myCfg.apiConfig.portA service may also declare symbolic ports as a comma-separated list and bind an API to one of them:
port: rest, admin
impl api PublicAPI as publicApi on restPort symbols and configuration paths must resolve unambiguously.
6. Use and Implementation Declarations
Section titled “6. Use and Implementation Declarations”Services bring model elements into local scope through injection declarations.
use declares a dependency consumed by the service.
impl api declares an API contract fulfilled by the service.
impl gateway api declares an API contract fulfilled by the service as a
gateway façade. It exposes one public API while routing its operations to used
APIs automatically where their contracts match, and to explicitly connected
expressions, broker topics, or other supported targets where they do not.
impl integration declares a reusable integration module hosted by the
service. The service remains the runtime and deployment boundary.
use api → service calls or depends on an APIimpl api → service exposes and fulfills an APIAliases provide the local identity used in connections and calls.
7. API Dependencies
Section titled “7. API Dependencies”use api PartnerAPIor:
use api PartnerAPI as partnerA used API represents an outbound dependency. Its operations may be referenced through the local alias or available name according to alias-resolution rules.
use api does not bind a listening port.
8. API Implementations
Section titled “8. API Implementations”impl api OrderAPI as api on myCfg.apiConfig.portimpl api declares that the service fulfills the named API contract. The on <target> clause is required.
The target may be:
- a literal numeric port, such as
8080; - a valid configuration path resolving the port value, such as
myCfg.apiConfig.port(the common form); - a port declared by
port:.
An implemented API operation can serve as a connection source for incoming requests and as a target for corresponding output behavior, subject to the API contract.
8.1 Gateway Implementations
Section titled “8.1 Gateway Implementations”A gateway is still a service: it owns a deployable process, a public API contract, configuration, and ordinary service dependencies. Its special behavior is bulk forwarding from the public façade API to compatible used APIs.
@service
GatewayService use config OceanGatewayConfig as gwCfg
use api BundleApi as bundle use api EntitlementApi as entitlement use api IdentityApi as identity use broker OceanBroker as broker use expression EngineValidate
impl gateway api OceanApi as api on gwCfg.apiConfig.port including bundle, entitlement, identity
# Explicit connections override automatic API forwarding. connect api.validate -> EngineValidate connect api.login -> broker.auth.loginThe public API (OceanApi above) is the contract exposed by the gateway. Every
name in including is the local alias of a use api declaration. An included
API supplies automatic targets for public operations with the same operation
name and compatible input, output, and error contracts. The public route and
documentation remain those of the gateway’s implemented API; target APIs are
not exposed directly through this declaration.
An explicit connect whose source is an operation of the gateway API takes
precedence over a matching automatic forwarding target. This permits a gateway
to replace a default forwarding route with an expression, broker topic,
aggregate, or another supported target without duplicating every unrelated
route.
The initial gateway model permits one impl gateway api declaration per
service. A public façade API can still compose operations from any number of
included APIs and explicit targets.
Gateway validation requires that:
- each
includingname resolves to a distinct used API alias; - every automatic match is unique and contract-compatible;
- explicit gateway connections are compatible with their public operation;
- every public operation resolves either through an explicit connection or one included API;
- a service does not implement the same public API both as a regular API and as a gateway.
Ambiguous automatic targets, unresolved public operations, duplicate public routes, and incompatible overrides are validation errors.
Gateway-wide concerns belong at the gateway boundary. Gateway policy declarations for CORS, authentication, authorization, request limits, rate limits, tracing, resilience, and error normalization are a planned extension to this model; they are not part of the initial gateway DSL capability and are not inferred from included APIs. Domain-level authorization may still be enforced by downstream services.
8.2 Hosted Integrations
Section titled “8.2 Hosted Integrations”impl integration PurchaseOrderIntegration as orchestrationThe alias is required and identifies the hosted integration instance within the service. A service may host one or more integrations, including a service whose only behavior is hosted integrations.
Integrations contribute their flow behavior, implemented contracts, broker interactions, aggregations, and schedules to the hosting service. The service supplies the deployable process, runtime configuration, and infrastructure bindings. An integration cannot be deployed directly.
9. Config and Context
Section titled “9. Config and Context”Configuration and context declarations require explicit aliases:
use config AppConfig as cfguse context RequestContext as ctxConfiguration supplies typed runtime settings to the deployable service. Context supplies supported execution or request-scoped information.
Aliases must be unique within the service.
10. Brokers and Databases
Section titled “10. Brokers and Databases”Broker aliases are required:
use broker OrderBroker as brokerDatabase aliases are optional:
use database PurchaseOrder as ordersuse database PartyBrokers expose typed topics or messaging patterns. Databases expose typed queries, commands, or entities according to their own DSL contracts.
11. Expressions, FSMs, and Components
Section titled “11. Expressions, FSMs, and Components”use expression OrderInituse expression SystemInit as systemInituse fsm OrderFSM as orderFsmuse component OrderFlow as orderFlowFSM aliases are required. Expression and component aliases are optional.
Expressions provide stateless logic, FSMs provide stateful behavior, and components provide stateless behavioral composition.
12. Functions and Vaults
Section titled “12. Functions and Vaults”use function HashPassword as hashuse vault MainVault as secretsFunction and vault aliases are optional.
A use function may also bring in a generic function type, aliased to a local
name the service then wires through connections:
use function Supplier<String> as GetDayuse function Func<TimeDateRequest, TimeDateResponse> as GetTimeDateSupplier<T> is a no-argument producer of T; Func<In, Out> is a one-argument
function. The alias can be used as a connection source or passed as a named
argument to an expression (see Section 15).
Functions expose supported callable behavior. Vault use makes the vault’s typed secret declarations available to the service according to the vault and generator contracts; secret values remain externally managed.
13. Alias Rules
Section titled “13. Alias Rules”Explicit aliases are required for:
use config;use context;use broker;use fsm.impl integration.
Aliases are optional for:
use api;impl api;use database;use expression;use component;use function;use vault.
All effective local names must be unique. When an alias is declared, service references should use that local identity consistently.
14. Initialization
Section titled “14. Initialization”init declares initialization behavior:
init systemInit(config:cfg)init OrderInitInitialization entries preserve declaration order.
They must resolve to expressions or other supported callable elements available in the service scope, and supplied arguments must satisfy the target signature.
Initialization occurs as part of service startup according to the target runtime contract.
15. Connections
Section titled “15. Connections”Connections route typed values or signals between service elements.
connect <source> -> <target>Sources and targets use injected aliases and their exposed operations, ports, topics, commands, queries, inputs, or outputs.
Common target forms seen in the examples:
# API operation to an expression (by alias or by name)connect api.getInfo -> infoconnect api.submitInvoice -> SubmitInvoice
# API operation straight to a database query or commandconnect api.getItem2 -> TodoItem.findByIdconnect api.deleteUser -> User.deleteUser
# API operation to an FSM event (via the fsm alias)connect api.approveInvoice -> fsm.approve
# Broker topic to an expressionconnect svcBroker.question.day -> GetDayString
# Binding a config sub-object to a used databaseconnect myCfg.dbConfig -> appDbA connection target may also be an expression call with named arguments, where each argument is bound to a used alias:
connect svcBroker.dayTip -> HandleDayTip(ctx:svcCtx)connect api.getTimeAnswer -> HandleTimeQuestion(ctx:svcCtx, getday:GetDay, getTimeDate:GetTimeDate)16. Connection Compatibility
Section titled “16. Connection Compatibility”Every connection is type-checked.
Without a mapper, the source output and target input must be compatible under the Ocean type system.
Conceptually:
source output type ── compatible with ── target input typeUnresolved endpoints, invalid direction, incompatible types, or ambiguous aliases are validation errors.
17. Mappers
Section titled “17. Mappers”A connection may declare a mapper when source and target types require explicit transformation.
Conceptual form:
connect <source> -<mapper>-> <target>Example:
connect api.createOrder -MapOrderRequest-> orderFlow.orderRequestThe mapper must resolve to a compatible expression or function. Exact mapper parsing and invocation must follow the common connection grammar implemented by Ocean.
18. Fan-out
Section titled “18. Fan-out”A source may connect to multiple targets where supported:
connect orderFlow.orderCreated -> broker.orderCreated, orders.createEach target is validated independently. Fan-out does not relax type compatibility or endpoint-direction rules.
Current binding modifiers may restrict a connection line to one bind even when it contains multiple targets.
19. Common Connection Directions
Section titled “19. Common Connection Directions”Supported service flows include:
| Source | Target | Meaning |
|---|---|---|
| API input | Component, FSM, or expression input | Route an incoming operation to behavior |
| API input | Database query or command | Serve an operation directly from the entity store |
| API input | FSM event (via the fsm alias) | Drive a state transition from a request |
| Component or FSM output | API output | Produce an API response |
| Broker topic | Component, FSM, or expression input | Consume a message |
| Component, FSM, or expression output | Broker topic | Publish a message |
| Component or FSM output | Database command | Persist or mutate data |
| Database query result | Component or FSM input | Route retrieved data |
| Component output | Component input | Chain composed behavior |
The referenced element contracts determine the precise valid directions and types.
20. Broker Patterns and Binds
Section titled “20. Broker Patterns and Binds”Broker connections may carry a pattern-specific bind such as repetition or
timeout. The bind is appended with &, and durations use the <value>:<unit>
form:
connect api.method -> broker.topicconnect GetDayTip -> svcBroker.dayTip & each(10:s)connect GetTimeDate -> svcBroker.question.timedate & timeout(3:s)Publish targets must use publish-capable topics; subscription sources must use subscribe-capable topics; request-response flows must use compatible request-response topics.
Only one bind per connection line is currently supported. (A , in a connection
separates multiple fan-out targets — see Section 18 — and is distinct from the
& bind.)
21. Aggregation
Section titled “21. Aggregation”aggregate combines values from several sources into one typed result, then
optionally delivers that result to one or more targets. It is distinct from a
point-to-point connect.
aggregate <name> trigger <implemented-api-call> schedule <named-or-inline-schedule>
result: <type>
collect trigger [using <expression>] collect <source> [using <expression>]
deliver <target> [using <expression>]
respond [using <expression>] timeout: <duration>An aggregate has exactly one activation: either trigger or schedule. A
trigger is one implemented API call. collect trigger adds that API request
payload to the result.
result is required. For List<T>, collected values must resolve to T and
are retained in collect declaration order. For a record result, collected
values populate its fields in declaration order. Collectors may execute
concurrently; deliveries execute in declaration order. Mappers are optional
but must be type-compatible.
respond applies only to a non-Void API trigger. Without a mapper it returns
the result directly; with a mapper it converts the result to the API response
type. Scheduled aggregates do not respond.
Example: ingress.receivePurchaseOrder is the implemented API through which
PO-1 enters the service. po2.generatePurchaseOrder is a used API that
supplies PO-2, and orders.purchaseOrder.po3 is a broker request-response
source for PO-3. The aggregate collects the three orders, delivers their list
to Warehouse, then publishes a mapped shipment request.
aggregate ProcessPurchaseOrders trigger ingress.receivePurchaseOrder
result: List<PurchaseOrder>
collect trigger collect po2.generatePurchaseOrder collect orders.purchaseOrder.po3
deliver warehouse.receivePurchaseOrders deliver orders.shipment.request using PurchaseOrdersToShipmentRequest
respond using PurchaseOrdersToAcknowledgementConcise one-line form
Section titled “Concise one-line form”The one-line form remains available when an API call composes and returns one result without explicit deliveries:
aggregate api.getTimeStringAnswer -TimeAnswerToString-> day.getDay | td.getDate using PrettyDate | td.getTime using PrettyTime | tip.getDayTip using PrettyTip - timeout 2sIts equivalent named aggregate is:
aggregate GetTimeStringAnswer trigger api.getTimeStringAnswer result: TimeAnswer
collect day.getDay collect td.getDate using PrettyDate collect td.getTime using PrettyTime collect tip.getDayTip using PrettyTip
timeout: 2s respond using TimeAnswerToStringIn the one-line form, the left-side API call maps to trigger, each pipe
target maps to collect, per-target mappers remain collect mappers, and the
left-side mapper maps to respond using.
22. Imports
Section titled “22. Imports”The service section supports imports of:
api;broker;expression;fsm;component;integration;config.
Example:
@service
@import api O.std.order.OrderAPI@1.0.0 as OrderAPI@import fsm O.std.order.OrderFSM@1.1.0 as OrderFSM@import expression O.std.validation.Validator@2.0.0 as Validator@import component O.core.AuditTrail@3.0.0 as AuditTrail@import integration O.partner.orders.OrderSync@1.0.0 as OrderSyncThe same import form may use P. references for local Pre-baked items where an item of the imported type is available.
Imported definitions are referenced through their declared import aliases. An imported integration is hosted exactly like a local one:
impl integration OrderSync as orderSync23. Includes
Section titled “23. Includes”A service file may include compatible @service definitions through the common @include mechanism.
Included and local services form one logical service section and are validated together. Cross-section inclusion is invalid.
The exact reference, placement, transitivity, and cycle rules are defined by dsl.include.
24. Complete Example
Section titled “24. Complete Example”A complete @service file
(ocean-examples/0001-task-manager/app-todo-service.ocn):
@service
TodoService
@perspectives: version:0.1.0, lifestyle:stable
use config TodoServiceConfig as myCfg impl api TodoApi as api on myCfg.apiConfig.port use database TodoDB as appDb use expression GetInfo as info use expression GetItem as get use expression GetAll as all use expression CreateItem as crt use expression AdjustItem as adj use expression DeleteItem as dlt
connect myCfg.dbConfig -> appDb
connect api.getInfo -> info connect api.getItem -> get connect api.getItem2 -> TodoItem.findById connect api.findItemByTitle -> TodoItem.findByTitle connect api.findItemByTitleAndPriority -> TodoItem.findByTitleAndPriority connect api.findItemByTitleOrPriority -> TodoItem.findByTitleOrPriority connect api.listItems -> all connect api.createItem -> crt connect api.adjustItem -> adj connect api.deleteItem -> dltThis example demonstrates:
- a
@perspectives:metadata line on the service; use configwith an alias andimpl api ... on <configPath>;use databaseand binding its config sub-object withconnect myCfg.dbConfig -> appDb;- aliased
use expressiondeclarations; - connections from API operations to expressions (by alias) and straight to
database queries/commands (
TodoItem.findById).
For brokers, context, use function types, expression-call targets, and &
bind modifiers, see ocean-examples/0003-answer-async/50-ans-service.ocn and
Sections 12, 15, and 20.
25. Deployment Boundary
Section titled “25. Deployment Boundary”A service is deployable, but deployment policy is defined separately.
@service defines what the unit contains and how it behaves. @deploy determines how a service is materialized for an environment or target.
@service → logical deployable unit@deploy → environment and target materializationGenerators may derive code, configuration, infrastructure integration, and packaging from the combined model.
26. Validation
Section titled “26. Validation”Validation includes:
- valid and unique service names;
- resolution of every used or implemented item;
- required aliases and alias uniqueness;
- required port binding for every implemented API;
- port and configuration-path resolution;
- API dependency versus fulfillment semantics;
- initialization target and argument validation;
- connection endpoint and direction validation;
- source/target type compatibility;
- mapper resolution and signature compatibility;
- broker-pattern compatibility;
- one-bind-per-connection limitation;
- aggregation validation;
- import and include validation;
- section-specific metadata validation.
Failures must identify the service and offending declaration.
27. Invalid Examples
Section titled “27. Invalid Examples”Missing implemented-API port:
impl api PublicAPI as apiMissing required alias:
use config AppConfigAbbreviated keywords:
use expr Validatoruse comp AuditTrailThese are invalid; use use expression and use component.
Type-incompatible connection:
connect api.createOrder -> orderFlow.integerInputInvalid when the source and target types are incompatible and no valid mapper is supplied.
28. Rules and Constraints
Section titled “28. Rules and Constraints”- A service file starts with
@service. - A file may define multiple services.
- Services are deployable orchestration boundaries.
use apideclares an API dependency.impl apideclares API fulfillment and requireson <target>, commonly a configuration path such asmyCfg.apiConfig.port.impl integration <IntegrationName> as <alias>hosts reusable orchestration; its alias is required and unique within the service.- Hosted integrations contribute behavior to the service but are not independent deploy targets.
- An implemented-API port may be a literal numeric port, a declared symbol, or a resolvable configuration path.
- An optional
@perspectives:(and@tags:) line may follow the service name. use functionmay bring in a named function or a function type (Supplier<T>,Func<In, Out>); a function type requires an alias.- A connection bind modifier is appended with
&(e.g.& timeout(3:s),& each(10:s));,separates fan-out targets. - A connection target may be an expression call with named arguments bound to aliases.
- Config, context, broker, and FSM uses require aliases.
- Database, expression, component, function, vault, and API aliases are optional.
- Effective local names are unique within a service.
- Initialization order is preserved.
- Connections use resolvable local endpoints.
- Connections are directional and typed.
- Incompatible types require a valid explicit mapper.
- A source may target multiple endpoints where supported.
- Only one bind per connection line is currently supported.
- Broker endpoints must match their declared messaging patterns.
- Aggregations follow the common aggregation grammar.
- Supported service imports are API, broker, expression, FSM, component, and config.
- Imported definitions are referenced through their import aliases.
- Same-section composition follows
dsl.include. - Deployment policy belongs to
dsl.deploy.
29. Related Knowledge
Section titled “29. Related Knowledge”concept.control-structure— explains the service’s role in Ocean behavioral composition.dsl.api— defines consumed and implemented API contracts.dsl.broker— defines messaging topics and patterns.dsl.database— defines persistence commands, queries, and entities.dsl.expression— defines stateless logic and mappers.dsl.fsm— defines stateful behavior.dsl.component— defines stateless behavioral composition.dsl.config— defines typed runtime configuration.dsl.context— defines supported contextual data.dsl.vault— defines typed secrets used by services.dsl.importanddsl.include— define reuse mechanisms.dsl.deploy— defines deployment materialization.
These relationships are declared in the metadata.