Common CMN syntax
Several CMN declarations share descriptions, labels, stereotypes, properties, and references. This page defines those common forms and separates grammar syntax from names supplied by profiles and facilities.
1. Documentation, descriptions and examples
CMN documentation is model input. Generators can use it for API contracts, source comments, diagrams or other target artifacts. It is different from a source-only comment and from a quoted display label.
1.1 Documentation delimiters
A documentation comment can precede many named model elements:
package com.example.customer
/**
* A customer visible through the public API.
*/
type Customer {
}The conventional leading * on each line is optional. CMN also supports rich documentation delimited by triple apostrophes:
package com.example.customer
'''
A customer visible through the public API.
'''
type Customer {
}Both forms create the same normalized model documentation. Prefer triple apostrophes for larger structured blocks and /** ... */ for short prose near a declaration.
// ... and ordinary /* ... */ comments are ignored by the model and are suitable for source-only notes. Do not place contract information in them.
The [[ ... ]] form used in component flows is implementation pseudocode, not a general description delimiter. Use it only in grammar positions that accept component commands.
1.2 Plain and explicit descriptions
Text before the first structured marker is the documentation's description:
package com.example.customer
'''
Customer data exposed through the public API.
'''
type Customer {
}Use @description when the description should be separated explicitly from other sections. An explicit @description supplies the effective description; it does not append a second description to preceding top-level prose:
package com.example.customer
'''
Internal authoring note.
@description Customer data exposed through the public API.
'''
type Customer {
}The public structured section markers are:
| Marker | Purpose |
|---|---|
@description | effective description of the documented element |
@summary | short operation summary |
@title | human-readable title where the consuming generator supports one |
@request | request-specific description and examples |
@response | description and examples for the operation's primary CMN result |
Write section markers without a trailing colon. For example, use @request, not @request:. The colon form remains a deprecated compatibility alias and produces a warning.
1.3 Examples
An example starts with the keyword example, not an @ marker. The shortest form supplies one unnamed value:
package com.example.customer
'''
Customer identifier.
example: 00000000-0000-4000-8000-000000000001
'''
type CustomerId specialization of StringPut a stable name between example and : when the generated contract should contain a named example:
package com.example.customer
'''
Customer identifier.
example standard: 00000000-0000-4000-8000-000000000001
'''
type CustomerId specialization of StringMultiline and structured values
A multiline value normally follows directly below example ...:. The value: boundary is optional; omit it when the example has no separate description. This is the usual form for an object request, including an object that contains another complex type:
package com.example.customer
import org.joinedworkz.facilities.common.base
import org.joinedworkz.facilities.common.base.api
platform Base
type NotificationSettings {
emailEnabled*: Boolean
preferredLanguage*: String
}
type Customer {
id**: String
displayName*: String
notificationSettings*: NotificationSettings
}
resource /customers as Customer[] by id {
'''
@request
example standard:
{
"id": "00000000-0000-4000-8000-000000000001",
"displayName": "Sample Customer",
"notificationSettings": {
"emailEnabled": true,
"preferredLanguage": "en"
}
}
'''
create() consumes=Customer
}The example text is not required to use JSON. For an application/json contract, however, use valid JSON as above so that the Base Facility can emit a structured OpenAPI value instead of a string.
Use the explicit value: boundary when the same example has a separate indented, possibly multiline description. Text before the boundary is the example description; text after it is the value:
package com.example.customer
'''
example standard:
A complete customer object.
It represents a normal successful request.
value:
{
"id": "00000000-0000-4000-8000-000000000001"
}
'''
type Customer {
id: String
}A short description can alternatively follow the declaration on the same line, with the value on the following lines. The first line is then the description; value: is still unnecessary:
package com.example.customer
'''
example standard: A complete customer object.
{
"id": "00000000-0000-4000-8000-000000000001"
}
'''
type Customer {
id: String
}Examples can be top-level documentation entries or belong to @request and @response. Section markers align with the normal documentation indentation; their descriptions and examples are indented beneath them. A later section marker ends the preceding section.
The primary CMN result is the operation's normal result inherited from its method type or declared by the concrete operation. A target facility can map it to a concrete success status such as 200 or 201. The term does not mean the OpenAPI default: response key.
@example: is a deprecated compatibility alias. New models use example: or example <name>:. An example value remains text in the canonical model. A facility decides how that text maps to its target representation; the Base OpenAPI contract defines the JSON behavior and precedence used by the Base cartridge.
1.4 Where documentation is consumed
The CMN grammar permits documentation on many declarations, but each target facility defines which metadata affects its generated artifacts. The model transformation retains normalized documentation for the public element kinds used by Base, Java and SpringBoot, including types, fields, services, operations, resources, resource methods, responses, components and provided boundaries.
For API-specific placement, see Representations and responses and OpenAPI documentation and examples.
2. Names and labels
Most declarations have an identifier used for references:
package com.example.customer
type Customer {
}Some declarations can additionally have a quoted, human-readable label:
package com.example.customer
type Customer 'Customer account' {
}Customer is the technical name; 'Customer account' is the label. Whether a generator emits the label is generator-specific.
Resource path segments can contain dashes and are not ordinary model-element identifiers:
package com.example.customer
resource /customer-accounts {
}CMN permits selected grammar words as identifiers in unambiguous positions. Avoid relying on that flexibility for public names when a conventional identifier is clearer. See the language-element index for the keyword boundary.
3. Abstract declarations
The grammar permits abstract on packages, simple types, enums, complex types, services, resources, and widgets:
package com.example.customer
abstract type Party {
}
abstract service PartyService {
}
abstract resource /parties {
}abstract is a grammar marker whose transfer and generated effect vary by element and facility. Service abstraction is not transferred to the canonical Core Model. Do not assume that an abstract declaration is omitted from output or becomes a target-language base type unless the selected facility documents and verifies that behavior.
Components and applications do not have an abstract position in the grammar.
4. Stereotypes
A stereotype specification follows the element keyword and precedes its name:
package com.example.customer
import org.joinedworkz.facilities.common.base
platform Base
type<entity> Customer {
id**: Id
}The angle brackets are CMN grammar. entity is a stereotype supplied by the Base profile. A stereotype is usable only on element kinds allowed by its profile declaration.
The grammar permits several stereotypes separated by commas and permits ! before an excluded stereotype:
package com.example.customer
type<first,!second> CustomerView {
}first and second are illustrative profile-defined names. They are not provided by CMN itself. JoinedWorkz does not transfer every additional or excluded stereotype reliably to the canonical Core Model. Use a single positive stereotype for public, portable models unless a facility explicitly documents another form.
A stereotype specification can also select a model-defined style after /:
<stereotype/styleName>Style declarations and their generated effect are specialized features. The style selection is parser syntax but is not transferred as a general generation contract; do not rely on it for public output.
5. Properties
Many declarations accept properties after their structural syntax:
package com.example.customer
type Example
stringValue='text'
integerValue=42
decimalValue=-1.5
booleanValue=true
missingValue=undefined
severalValues='first','second'
°enabledThis example demonstrates grammar forms; the property names are illustrative. A selected profile must define or otherwise support a property before a model can rely on its semantics.
Property values can be:
- strings in single or double quotes;
- integers and decimals, including negative values;
trueorfalse;- references to model elements, optionally with
[]or[KeyType]type cardinality; undefined;- comma-separated lists of values.
°enabled is a flag with the implicit Boolean value true. The alternate name:=value form marks the value as constant for property processing:
package com.example.customer
type CustomerNumber specialization of String maxLength:=32Property placement is defined by the CMN grammar; the name, expected value type, default, inheritance, and generated effect are profile- or facility-defined.
Facility Profiles can also control whether a model property is copied from a type, included field, stereotype, method type or referenced model element. Facility authors can find the advanced contract in Profile-controlled property propagation. This model-property behavior is unrelated to project keys in joinedworkz.properties.
6. Type arguments
Simple types can declare positional arguments by referencing properties:
package com.example.types
type Text(maxLength) maxLength=1000A use of the type supplies values in the same order:
package com.example.customer
type Address {
street: Text(200)
}The declaration and call syntax are core CMN. Which arguments a facility-provided type exposes is part of that type's model definition.
7. References
References can resolve:
- a simple name in the current or an imported package;
- an alias-qualified name such as
shared::Timestamp; - a dotted qualified name where that grammar position accepts it.
package com.example.customer
import com.example.shared as shared
type AuditInfo {
createdAt: shared::Timestamp
}Different constructs restrict the referenced element kind. A field type must resolve to a type, a component dependency to a type, service, or component, and a resource method to a resource method type. A syntactically valid name with the wrong target kind is therefore not a valid linked model.
Imports, aliases, and package rules are described in File header & packages.
8. Cardinality and optionality
The absence of an optional grammar fragment is different from model cardinality:
- optional declaration clauses can be omitted when the grammar permits;
- without a suffix, no explicit minimum or maximum is stored in the Core Model; current facilities normally treat this as an optional single value;
*,[], ranges, and dictionary cardinalities describe occurrence or collection semantics;- key
**is a field marker, not a general mandatory marker.
See Complex types for field cardinalities and Resources & method types for resource, parameter, and response cardinalities.
