Skip to content
Ocean-Atlasv0.1.0Canonical Knowledge

Datatype DSL Reference

The @datatype section defines reusable data structures used throughout Ocean.

Datatypes provide technology-independent data-transfer-object (DTO) contracts that can be used by APIs, services, brokers, expressions, databases, and other Ocean constructs.

A datatype file:

  • starts with @datatype;
  • must define at least one datatype-related item;
  • may contain multiple definitions;
  • may contain comments;
  • must not nest datatype definitions.

Example:

@datatype
PurchaseOrder
buyer : Party
seller : Party
item : Item

A datatype consists of a name followed by its fields.

<DatatypeName>
<fieldName> : [*]<FieldType>
...

Each field:

  • is declared on a separate line;
  • consists of a field name and type separated by :;
  • may use the * prefix to declare the field as a pointer.

Example:

PurchaseOrder
buyer : Party
seller : *Party
item : Item

Datatype definitions may appear sequentially in the same @datatype file but must not be nested.

2.2 Self-Referential and Recursive Datatypes

Section titled “2.2 Self-Referential and Recursive Datatypes”

A datatype may reference itself, directly or indirectly, to model recursive structures such as trees, linked lists, and nested categories.

A direct single-value self-reference must use the * pointer prefix so the datatype does not require infinite inline expansion:

Category
name : String
parent : *Category

Self-reference through a compound datatype is also allowed because the collection provides the required indirection:

Category
name : String
children : List<Category>

Indirect recursive relationships are allowed when the cycle contains at least one pointer or compound-type edge:

Employee
team : *Team
Team
members : List<Employee>

The following direct inline self-reference is invalid:

Category
parent : Category

Rules:

  • The referenced datatype must resolve within the file or through an available datatype definition.
  • A direct single-value self-reference must use *.
  • Every indirect recursive cycle must contain at least one pointer or compound datatype edge.
  • Recursive collections may use List, Set, or Map where their element or value types are otherwise valid.
  • Recursive datatype structures are distinct from interface cycles; circular interface relationships remain prohibited.
  • Generators must preserve recursive structure without infinitely expanding the datatype definition.

Primitive datatypes may be used directly without explicit definition.

Datatype Definition Example
Int Whole number without a decimal point 42
Float Single-precision decimal number 3.14
Double Double-precision decimal number 3.1415926535
Boolean Boolean value true
String Sequence of characters "Hello"
Date Calendar date without time 2025-04-16
Time Time of day without a date 14:30:00
DateTime Date and time without timezone information 2025-04-16T14:30:00
Timestamp Date and time including timezone or offset 2025-04-16T14:30:00Z
Json Arbitrary structured JSON data whose schema is not explicitly modeled as an Ocean datatype {"name":"Ocean","version":1}

Json represents structured JSON data rather than a JSON-encoded String.

The concrete representation of each primitive is determined by the target technology or language generator.


Compound datatypes represent collections of other datatypes.

Datatype Definition Example
List<Datatype> Ordered collection that may contain duplicate elements List<Int>
Set<Datatype> Collection of unique elements Set<String>
Map<Key, Value> Collection of key-value pairs Map<String, Int>

Examples:

Order
items : List<Item>
tags : Set<String>
attributes : Map<String, String>

Compound types may contain primitive or user-defined datatypes where supported by the type.


Datatype names must match:

^[A-Z][A-Za-z0-9_]*$

They must:

  • begin with an uppercase letter;
  • contain only letters, digits, and _.

Examples:

Input Valid
Apple Yes
A_123 Yes
X1 Yes
Test_Name Yes
apple No
_Test No
1Start No

Field names must match:

^[a-z][A-Za-z0-9_]*$

They must:

  • begin with a lowercase letter;
  • contain only letters, digits, and _.

Examples:

Input Valid
apple Yes
a_123 Yes
x1 Yes
test_Name Yes
Apple No
_test No
1start No

A field type must resolve to one of:

  • a primitive datatype;
  • a compound datatype;
  • a user-defined datatype;
  • an enum;
  • another compatible datatype construct supported by @datatype.

Datatypes may declare an ID field with a structured generation pattern.

<DatatypeName>
id pattern <pattern>

Example:

PurchaseOrder
id pattern PO-UUID

A pattern may consist of:

[prefix-]<strategy>

The prefix is optional and provides a domain-specific identifier prefix.

Strategy Description Example
UUID Universally unique identifier 550e8400-e29b-41d4-a716-446655440000
ULID Lexicographically sortable unique identifier 01F8MECHZX3TBDSZ7XRADM79XE
AutoInc Auto-incrementing integer within the applicable scope 42
Timestamp Milliseconds since epoch 1684170839000
Random6 Six-character random alphanumeric identifier X7F3C9

Examples:

Pattern Example
UUID 7f3a9b12-8d54-4a5c-a3e1-d3c6ea9d4123
PO-UUID PO-7f3a9b12-8d54-4a5c-a3e1-d3c6ea9d4123
USR-Random6 USR-X8Z1QW
TX-Timestamp TX-1684170839000
DOC-AutoInc DOC-42

Enums define a fixed set of named values that may be used as field types.

They are suitable for statuses, categories, types, and other constrained value sets.

enum <EnumName>
<value>
...

Example:

@datatype
enum OrderStatus
created
confirmed
shipped
delivered
cancelled
PurchaseOrder
status : OrderStatus
  • Enum names use UpperCamelCase.
  • Enum values use lowerCamelCase.
  • Enum values are simple and unparameterized.
  • Enum values are represented as strings.
  • Enums may be referenced as datatype field types.
  • An enum must be defined or made available before it is used by a datatype.

Ocean generators ensure that an enum has an unknown value suitable for use as its zero/default value.

If unknown is not explicitly declared, it is automatically added as the first enum value.


An error defines a standardized first-class failure model.

Errors provide a consistent, transport-independent representation of failures across APIs, brokers, expressions, services, and other boundaries.

Errors are data rather than language-level exceptions.

They are serializable, transport-independent, composable, and stackable. Their controlled structure allows generators and applications to expose errors safely across service boundaries. Errors are typically carried by an envelope, but may also be used independently where appropriate.

error <ErrorName>
code : <code>
message : <message>
httpCode : <httpCode>

Example:

@datatype
error NotFoundError
code : NOT_FOUND
message : Not found
httpCode : 404
Field Type Required Description
code String Yes Stable machine-readable error identifier
message String Yes Human-readable error summary
details List<String> No Contextual details accumulated during propagation
httpCode Int No HTTP status hint; defaults to 500
  • code should use UPPER_SNAKE_CASE.
  • code is a stable, machine-readable identifier and message is its human-readable summary.
  • httpCode defaults to 500 when omitted.
  • details may be populated while an error propagates through runtime layers.
  • Error definitions are immutable, while runtime error instances may be enriched with contextual details.
  • httpCode is a transport hint and does not make the Error model HTTP-specific.

The same error definition may be reused across different transport and execution technologies.


An envelope defines a standardized operation result containing either successful data or an error.

It provides consistent result semantics across APIs, brokers, asynchronous messaging, and internal service boundaries.

envelope <EnvelopeName>
data <Datatype>

Example:

@datatype
envelope LoginResult
data AuthTokens

Every envelope conceptually contains:

ok : Boolean
data : <Datatype>
error : Error
meta : Map<String, String>

These fields are implicit and are not redeclared for each envelope.

When:

ok == true

then:

  • data must be present;
  • error must be null.

When:

ok == false

then:

  • error must be present;
  • data must be null.

Generated code and validators enforce these invariants.

  • An envelope has one explicitly declared successful data type.
  • At runtime an envelope contains either successful data or an error, never both.
  • Error handling is standardized through the Error model.
  • The implicit meta map is optional and supports concerns such as tracing, correlation IDs, and warnings.
  • Ocean uses explicitly named envelopes rather than DSL generic syntax such as Envelope<T>.

@datatype supports interface-based structural composition.

Interfaces define reusable field contracts. They provide structural compatibility without behavior or method inheritance.

An interface may be declared explicitly:

interface OrderAware
order : String

A datatype may conform to one or more interfaces using ::

PurchaseOrder : OrderAware, Timestamped
buyer : Party
seller : Party

Fields contributed by interfaces become part of the resulting datatype structure.

@datatype
interface OrderAware
order : String
Timestamped
createdAt : Date
updatedAt : Date
PurchaseOrder : OrderAware, Timestamped
buyer : Party
seller : Party
items : List<Item>
issuedOn : Date
status : String

PurchaseOrder therefore contains:

  • order;
  • createdAt;
  • updatedAt;
  • its explicitly declared fields.

It may also be used wherever structural compatibility with OrderAware or Timestamped is required.

  • Interfaces must not be nested.
  • Interfaces define fields only and must not define behavior or methods.
  • Conformance is structural.
  • Duplicate fields contributed by multiple interfaces must be deduplicated consistently.
  • Circular interface relationships are not allowed.

The interface keyword is optional where Ocean can infer interface usage from structural composition.


A @datatype file may import datatypes from Ocean packages.

@import datatype <reference> as <LocalName>

Example:

@datatype
@import datatype O.domain.payment.CardInfo@1.2.0 as CardInfo
Payment
card : CardInfo
  • Only datatypes may be introduced with @import in an @datatype file.
  • The as <LocalName> alias is required and is used to reference the imported datatype within the file.
  • Imported datatypes may be referenced like locally available datatypes.
  • Enums are included as part of datatype imports.
  • Behavioral or service-level constructs must not be imported as datatype definitions.

This restriction preserves the self-contained structural nature of datatype knowledge.


The following is a complete @datatype file (ocean-examples/0008-invoice-approval-system/10-datatype.ocn):

@datatype
Invoice
id pattern INV-UUID
title : String
customer : *Customer
totalAmount : Float
submittedAt : DateTime
approvedAt : DateTime
rejectedAt : DateTime
status : InvoiceStatus
createdBy : *User
approvedBy : *User
rejectedBy : *User
remark : String
enum InvoiceStatus
pendingApproval
approved
rejected
#paid
User
id pattern USR-UUID
name: String
role: String
Customer
id pattern CUS-UUID
name : String
department : String
SubmitInvoiceReq
title : String
customerId : String
totalAmount : Float
createdBy : String # userId
ApproveInvoiceReq
approvedBy : String # userId
remark : String
RejectInvoiceReq
rejectedBy : String # userId
reason : String
InvoiceSummary
id : String
title : String
customerName : String
totalAmount : Float
status : InvoiceStatus

This example demonstrates:

  • user-defined datatypes;
  • primitive datatypes (String, Float, DateTime);
  • pointer fields (*Customer, *User);
  • an enum used as a field type;
  • structured ID fields with prefixed generation patterns;
  • request and summary DTOs alongside the core domain types;
  • line and trailing comments.

For compound datatypes (List, Set, Map), the Json primitive, errors, envelopes, and interfaces, see Sections 3, 4, 8, 9, and 10.


The @datatype DSL is related to:

  • dsl.import — defines the mechanism for importing selected datatype definitions from the Ocean Repository.
  • dsl.include — defines the mechanism for including reusable datatype definitions from the Ocean Repository.

These semantic relationships are declared in the document metadata.