4. Layer 1 — Metamodel
Contents of DISL 0.1
The metamodel defines the abstract syntax of the language: the kinds of elements that can exist, their attributes, how they nest, how they connect, and which ports they expose. It says nothing about appearance.
{
"metamodel": {
"diagram": { "attributes": { "title": { "type": "string" } } },
"dataTypes": { "Email": { "base": "string", "format": "email" } },
"enums": { "Priority": { "values": { "low": {}, "normal": {}, "high": {} }, "ordered": true } },
"types": { "Task": { "attributes": { "title": { "type": "string", "required": true } } } },
"relations": { "DependsOn": { "source": "Task", "target": "Task" } }
}
}
| Property | Type | Description |
|---|---|---|
diagram |
{attributes, doc} |
Attributes of the diagram root element (title, author, version, settings). |
dataTypes |
map → DataType | Named value types with facets, or structs (4.4). |
enums |
map → Enum | Enumerations (4.5). |
types |
map → NodeType | Node types — everything that is drawn as a node, including containers, lanes and annotations (4.6). |
relations |
map → RelationType | Relation types — everything that is drawn as an edge (4.9). |
doc |
Doc | Documentation of the metamodel. |
| Type | CEL type | JSON representation | Facets |
|---|---|---|---|
string |
string |
string | minLength, maxLength, pattern, format |
text |
string |
string (multi-line) | as string, plus markup: "plain" | "markdown" |
int |
int |
integer | min, max, step, unit |
number |
double |
number | min, max, exclusiveMin, exclusiveMax, step, precision, unit |
bool |
bool |
boolean | – |
date |
timestamp |
"YYYY-MM-DD" |
min, max (as dates) |
datetime |
timestamp |
RFC 3339 string | min, max, timezone (stored zone policy: "utc", "preserve", IANA name) |
time |
duration since midnight |
"HH:MM[:SS]" |
min, max, step |
duration |
duration |
ISO 8601 duration | min, max, calendar (allow P1M/P1Y) |
color |
string |
CSS color string | palette (list of allowed colors), alpha (bool) |
uri |
string |
URI string | schemes (allowed schemes) |
expression |
string |
CEL source | context (name of the CEL context it will be evaluated in), resultType |
json |
dyn |
any JSON | schema (inline JSON Schema) |
binary |
bytes |
base64 string | mediaTypes, maxBytes |
Unit facets (unit) are informational strings ("h", "kg", "EUR"); runtimes SHOULD show them as field suffixes.
An attribute describes one named value on a node, relation, port or the diagram.
| Property | Type | Description |
|---|---|---|
type |
TypeName | Primitive, enum, data type, or node/relation type (a reference, 4.8). Required. |
label |
LocalizedText | Display name. |
doc |
Doc | Shown as field help. |
required |
bool | Must have a non-empty value. Enforced as a built-in constraint with severity error (section 8.7). Default false. |
default |
literal or {cel} |
Initial value on creation. A CEL default is evaluated in the create context (12.3). |
many |
bool | The attribute holds a list. Default false. |
minItems, maxItems, uniqueItems |
int, int, bool | List facets when many. |
readOnly |
bool | Not editable by users (may still be set by behavior). |
derived |
Expression | Computed, never stored; evaluated in the element context. Implies readOnly. |
transient |
bool | Editable but not persisted (for example UI-only flags). |
unique |
"diagram", "parent", "type" |
Value must be unique within the scope. Enforced as a built-in constraint. |
key |
bool | Part of the element's natural key (used for natural ids, 11.5, and for display). |
secret |
bool | Masked in forms and excluded from exports and logs. |
facets |
object | Primitive facets from 4.2 (min, max, pattern, …); may also be written directly on the attribute. |
group |
string | Default form section when a form is generated. |
order |
int | Default position in generated forms. |
x-* |
any | Extensions. |
{
"attributes": {
"name": { "type": "string", "required": true, "maxLength": 80, "unique": "parent",
"doc": "Unique within the enclosing region." },
"priority": { "type": "Priority", "default": "normal" },
"effort": { "type": "number", "min": 0, "step": 0.5, "unit": "h" },
"tags": { "type": "string", "many": true, "uniqueItems": true },
"slug": { "type": "string", "derived": "self.name.lowerAscii().replace(' ', '-')" },
"owner": { "type": "Person", "doc": "Reference to a person node in the same diagram." },
"createdAt": { "type": "datetime", "readOnly": true, "default": { "cel": "env.now" } }
}
}
Unset values. Reading an attribute that has no stored value yields its default when one is declared, otherwise the zero value of its CEL type ('', 0, 0.0, false, [], {}, null for references, the Unix epoch for timestamps, 0s for durations). The CEL macro has(self.attr) tests whether a value is explicitly stored. Persistence omits unset values (11.8).
A data type is either a constrained primitive or a struct.
{
"dataTypes": {
"Percentage": { "base": "number", "min": 0, "max": 100, "unit": "%", "doc": "0–100 %" },
"Iso4217": { "base": "string", "pattern": "^[A-Z]{3}$", "doc": "Currency code" },
"Money": {
"fields": {
"amount": { "type": "number", "required": true },
"currency": { "type": "Iso4217", "default": "EUR" }
},
"display": { "cel": "string(value.amount) + ' ' + value.currency" }
}
}
}
| Property | Type | Description |
|---|---|---|
base |
primitive | For constrained primitives. |
| facets | Facets of the base primitive. | |
fields |
map → Attribute | For structs; fields may themselves be structs, lists or references. |
display |
Bindable<string> | How a value is summarised in labels and list cells (value is the struct). |
label, doc |
Struct values are CEL maps; fields are accessed as self.budget.amount.
{
"enums": {
"Priority": {
"ordered": true,
"values": {
"low": { "label": "Low", "color": "#639922", "icon": "arrow-down" },
"normal": { "label": "Normal" },
"high": { "label": "High", "color": "#E24B4A", "icon": "arrow-up",
"doc": "Must be scheduled in the current iteration." }
}
}
}
}
| Property | Type | Description |
|---|---|---|
values |
map → EnumValue (ordered) | Value ids are simple identifiers and are what is stored. Map order is the display order. |
ordered |
bool | Values have a meaningful order; enables <, > comparison via ordinal(value, 'Priority'). |
extensible |
bool | Users may enter values not listed (combobox). |
label, doc |
EnumValue: label, doc, color, icon, deprecated. The optional color and icon are hints that notations may use ({ "cel": "enumColor('Priority', self.priority)" }).
A node type describes a kind of element that is drawn as a node.
| Property | Type | Description |
|---|---|---|
label, doc, icon |
Display name, documentation, default icon. | |
abstract |
bool | Cannot be instantiated; exists for inheritance and type references. |
extends |
TypeRef or TypeRef[] | Supertypes (4.7). |
attributes |
map → Attribute | Own attributes. |
ports |
map → PortType | Connection points (4.10). |
children |
Containment | What may be nested inside (4.8). |
multiplicity |
{min, max} |
Number of instances allowed per diagram (for example exactly one Start). Enforced as a built-in constraint. |
viewOnly |
bool | The element has no model meaning and lives only in a view: notes, free text, frames, images, annotations. View-only elements are stored in view data (11.6) and are excluded from diagram.nodes unless includeViewOnly is used (12.4). |
labelAttribute |
attribute name | The attribute used as the element's name in lists, problem messages, reference pickers and default labels. Default: the first key attribute, else the first required string attribute, else the first string attribute. |
tags |
string[] | Free classification, usable in CEL as self.isTagged('x'). |
x-* |
Extensions. |
A type inherits all attributes, ports, containment rules and tags from its supertypes, and is accepted wherever a supertype is expected (in relation ends, containment, references, constraint scopes, notation lookups).
- Multiple inheritance is allowed. The set of supertypes MUST form a directed acyclic graph.
- A subtype MAY redeclare an inherited attribute to narrow it: tighten facets, change
default,labelordoc, setrequired: true, or narrow a reference type to a subtype. It MUST NOT change the base type or relax facets. Conflicting inherited declarations of the same attribute from two supertypes are a specification error unless the subtype redeclares it. - Notation, forms and constraints are looked up along the linearised supertype chain (C3 linearisation, as in Python): the most specific declaration wins, and a node type without its own notation uses its nearest supertype's notation.
- Relation types inherit from relation types only; node types from node types only.
Containment makes nodes nest: a pool contains lanes, a lane contains tasks, a table contains columns. A contained node has exactly one parent; deleting the parent deletes its contents (unless behavior says otherwise, 9.5).
{
"Lane": {
"attributes": { "name": { "type": "string" } },
"children": {
"allowed": ["Task", "Event", "Gateway"],
"min": 0,
"max": null,
"ordered": true,
"slots": {
"header": { "allowed": ["LaneHeader"], "max": 1 },
"content": { "allowed": ["Task", "Event", "Gateway"] }
}
}
}
}
| Property | Type | Description |
|---|---|---|
allowed |
TypeRef[] | Types that may be direct children. |
min, max |
int / null | Child count bounds (null = unbounded). |
perType |
map TypeRef → {min, max} |
Per-type bounds. |
ordered |
bool | Child order is meaningful and persisted (for example stacked compartments, table columns). |
slots |
map → {allowed, min, max, doc} |
Named sub-containers when a node has several distinct regions; each child records its slot. Notation maps slots to compartments or regions. |
acrossViews |
bool | Whether a child may be shown outside its parent in other viewpoints. Default false. |
A node type without children cannot contain other nodes. Top-level nodes have the diagram as parent (parent == null in CEL, self.owner == diagram).
References are attributes whose type is a node or relation type. They store the target's id, are resolved to elements in CEL, and are not containment: deleting the target sets the reference to null (or deletes the referencing element when behavior declares onDelete: cascade-references, 9.5). References are drawn as edges only if a notation says so (6.10, edges for reference attributes).
A relation type describes a kind of connection, drawn as an edge.
{
"relations": {
"Transition": {
"doc": "A change from one state to another, fired by a trigger.",
"source": { "types": ["State"], "max": null },
"target": { "types": ["State"], "exclude": ["InitialState"] },
"directed": true,
"allowSelfLoops": true,
"allowParallel": true,
"attributes": {
"trigger": { "type": "string" },
"guard": { "type": "expression", "context": "x-guard" }
}
}
}
}
| Property | Type | Description |
|---|---|---|
label, doc, icon |
||
abstract, extends |
As for node types. | |
source, target |
TypeRef, TypeRef[] or RelationEnd | Allowed endpoints. Required. |
directed |
bool | Default true. Undirected relations treat source/target as unordered for allowParallel and cycle checks. |
allowSelfLoops |
bool | Source and target may be the same element. Default false. |
allowParallel |
bool | More than one relation of this type between the same pair. Default true. |
acyclic |
bool | Shorthand for a built-in constraint forbidding cycles over this relation type. |
attributes |
map → Attribute | Relation attributes. |
connectsRelations |
bool | Endpoints may be relations, not only nodes (for example UML association classes, annotations pointing at edges). Default false. |
derived |
Expression | Derived relations are computed (list of {source, target} maps) and drawn read-only. |
viewOnly |
bool | As for node types — connectors between notes, for instance. |
RelationEnd
| Property | Type | Description |
|---|---|---|
types |
TypeRef[] | Allowed element types. |
exclude |
TypeRef[] | Subtypes excluded from types. |
ports |
string[] | Port types the end must attach to (4.10). If set, the end MUST be a port of that type. |
min, max |
int / null | How many relations of this type an element may have at this end (outgoing for source, incoming for target). |
role |
string | Role name, usable in CEL and labels (for example "parent" / "child"). |
doc |
Doc |
Connectivity rules that depend on attributes or on the pair of endpoints are written as connect constraints (8.4).
Ports are named connection points on nodes: pins of a circuit component, input/output handles of a data-flow block, the sockets of a UML component.
{
"Block": {
"attributes": { "name": { "type": "string" } },
"ports": {
"in": { "direction": "in", "multiplicity": { "min": 1, "max": 8 }, "dynamic": true,
"attributes": { "name": { "type": "string" }, "dataType": { "type": "string" } } },
"out": { "direction": "out", "multiplicity": { "min": 1, "max": 1 } }
}
}
}
| Property | Type | Description |
|---|---|---|
label, doc |
||
direction |
"in", "out", "inout" |
Allowed edge direction at this port. Default "inout". |
multiplicity |
{min, max} |
Number of port instances of this type per node. Default {min: 1, max: 1} (one fixed port). |
dynamic |
bool | Users may add and remove instances (within multiplicity). |
maxConnections |
int / null | Edges per port instance. |
accepts |
TypeRef[] | Relation types that may attach. Default: all whose ends allow this node type. |
attributes |
map → Attribute | Attributes of each port instance. |
Port instances are elements with kind == "port", an id, a type of the form Block.in, and an owner (the node). They are persisted with their owner (11.4).
Illustrated by
- Entity–relationship diagram: 5 items in this layer
- State machine: 11 items in this layer
- Project timeline: 8 items in this layer