Entity–relationship diagram
Example definition erd.dis: Tables with typed columns and relationships in crow's-foot notation.
It is explained in 17.3 Entity–relationship diagram — compartments from children, crow's-foot markers.
What it demonstrates
From 17.3 Entity–relationship diagram — compartments from children, crow's-foot markers:
This example shows notation driven by the model. Columns are child elements listed in a compartment, with conditional item styles and icons. Relationship markers are computed from the cardinality attributes (erZeroOrMany, erOne, …). The line is dashed for non-identifying relationships. Cardinality labels sit at both ends and only appear when zoomed in. Tables align to a 20 px grid while bendpoints use a 10 px grid. Ids are natural (derived from table and column names), persistence is YAML, and a plugin provides SQL export.
Views generated from the file
These views are drawn by this site from erd.dis itself: its metamodel, and the layers it fills. They are not the notation the definition draws; that appears in an IDE that runs it.
erd.dis at 4e69c28Diagram as text (Mermaid)
classDiagram
accTitle: Metamodel of the Entity–relationship diagram example
accDescr: Class diagram of erd.dis: 2 node types (Table, Column) and 1 relation types (Relationship), with their typed attributes, inheritance, containment and relation ends.
class Table {
+string name
+string schema
+text comment
}
class Column {
+string name
+SqlType dataType
+bool nullable
+bool primaryKey
+Column references
}
class Relationship {
<<relation>>
+string name
+Cardinality sourceCardinality
+Cardinality targetCardinality
+bool identifying
}
Table "1" *-- "1..*" Column : contains
Relationship --> Table : source
Relationship --> Table : targeterd.dis at 4e69c28Diagram as text (Mermaid)
flowchart LR
accTitle: Layers of erd.dis
accDescr: The eight DISL layers in order. erd.dis fills 8 of them: Metamodel (5 items), Coordinates (2 items), Notation (4 items), Toolbox (6 items), Constraints (3 items), Behavior (1 items), Layout (2 items), Persistence (6 items).
L1["1 · Metamodel<br/>5 items"]
L2["2 · Coordinate systems, placement and snapping<br/>2 items"]
L3["3 · Notation (visual definition)<br/>4 items"]
L4["4 · Toolbox, context tools and forms<br/>6 items"]
L5["5 · Constraints<br/>3 items"]
L6["6 · Behavior<br/>1 items"]
L7["7 · Layout<br/>2 items"]
L8["8 · Persistence<br/>6 items"]
L1 --> L2 --> L3 --> L4 --> L5 --> L6 --> L7 --> L8
classDef filled stroke-width:3px
classDef empty stroke-dasharray:4 4
class L1 filled
class L2 filled
class L3 filled
class L4 filled
class L5 filled
class L6 filled
class L7 filled
class L8 filled- Layer 1 — Metamodel: 5 items
- Layer 2 — Coordinate systems, placement and snapping: 2 items
- Layer 3 — Notation (visual definition): 4 items
- Layer 4 — Toolbox, context tools and forms: 6 items
- Layer 5 — Constraints: 3 items
- Layer 6 — Behavior: 1 items
- Layer 7 — Layout: 2 items
- Layer 8 — Persistence: 6 items
The file
Download erd.dis (9,674 bytes, unchanged from the source).
{
"$schema": "https://etalii.net/adp/disl/schema/0.1/disl.schema.json#/$defs/Specification",
"disl": "0.1",
"language": {
"id": "org.example.erd",
"version": "2.0.0",
"label": "Entity–relationship diagram",
"doc": "Tables with typed columns and relationships in crow's-foot notation."
},
"metamodel": {
"enums": {
"SqlType": {
"extensible": true,
"values": { "uuid": {}, "text": {}, "varchar": {}, "int": {}, "bigint": {}, "numeric": {},
"bool": {}, "date": {}, "timestamptz": {}, "jsonb": {} }
},
"Cardinality": {
"values": {
"one": { "label": "Exactly one" },
"zeroOrOne": { "label": "Zero or one" },
"oneOrMany": { "label": "One or many" },
"zeroOrMany": { "label": "Zero or many" }
}
}
},
"types": {
"Table": {
"attributes": {
"name": { "type": "string", "required": true, "key": true, "pattern": "^[a-z_][a-z0-9_]*$", "unique": "diagram",
"doc": "snake_case table name." },
"schema": { "type": "string", "default": "public" },
"comment": { "type": "text" }
},
"children": { "allowed": ["Column"], "ordered": true, "min": 1,
"slots": { "columns": { "allowed": ["Column"], "min": 1 } } }
},
"Column": {
"attributes": {
"name": { "type": "string", "required": true, "key": true, "unique": "parent" },
"dataType": { "type": "SqlType", "default": "text", "label": "Type" },
"nullable": { "type": "bool", "default": true },
"primaryKey": { "type": "bool", "default": false, "label": "Primary key" },
"references": { "type": "Column", "doc": "Foreign-key target column." }
}
}
},
"relations": {
"Relationship": {
"source": "Table",
"target": "Table",
"directed": false,
"allowSelfLoops": true,
"attributes": {
"name": { "type": "string" },
"sourceCardinality": { "type": "Cardinality", "default": "one" },
"targetCardinality": { "type": "Cardinality", "default": "zeroOrMany" },
"identifying": { "type": "bool", "default": false,
"doc": "Identifying relationships make the parent key part of the child key." }
}
}
}
},
"coordinates": {
"snapProfiles": {
"coarse": { "both": { "grid": { "spacing": 20 } }, "targets": ["objects", "rule"],
"doc": "Tables align to a 20 px grid; edges snap their bendpoints to 10 px.",
"bendpoints": { "both": { "grid": { "spacing": 10 } } } }
},
"systems": {
"canvas": { "kind": "cartesian", "x": { "kind": "linear" }, "y": { "kind": "linear" }, "snapping": "coarse",
"grid": { "visible": true, "style": "lines", "spacing": 20, "majorEvery": 5 } }
}
},
"notation": {
"styles": {
"table": { "fill": "#FFFFFF", "stroke": { "color": "#444441", "width": 1 }, "cornerRadius": 6,
"font": { "family": "Inter, sans-serif", "size": 12 }, "shadow": { "dx": 0, "dy": 1, "blur": 3, "color": "#00000022" } }
},
"nodes": {
"Table": {
"shape": { "type": "roundedRect", "params": { "radius": 6 } },
"style": "table",
"size": { "default": [220, 120], "autoSize": "fitHeight", "resizable": "horizontal" },
"labels": [
{ "id": "name", "text": { "attribute": "name" }, "position": "top", "editable": "inline",
"background": { "fill": "#EEEDFE", "padding": [6, 8] },
"style": { "font": { "weight": 600, "color": "#3C3489" } } }
],
"compartments": [
{ "id": "columns",
"items": { "children": ["Column"], "slot": "columns" },
"itemText": { "cel": "item.name + ' ' + item.dataType + (item.nullable ? '' : ' not null')" },
"itemIcon": { "cel": "item.primaryKey ? 'key' : (has(item.references) ? 'link' : '')" },
"itemConditions": [ { "when": "item.primaryKey", "style": { "font": { "weight": 600, "decoration": "underline" } } } ],
"separator": { "color": "#D3D1C7", "width": 0.5 },
"overflow": "ellipsis", "maxItems": 20,
"editable": { "add": true, "remove": true, "reorder": true, "inline": true },
"emptyText": "No columns" }
],
"anchors": { "mode": "sides", "spread": true },
"form": { "form": "columnsTable", "interaction": "onDoubleClick", "minZoom": 0.8, "fallback": "labels" },
"lod": [ { "maxZoom": 0.4, "hide": ["compartments"] } ]
},
"Column": { "shape": "none", "labels": [] }
},
"edges": {
"Relationship": {
"line": {
"stroke": { "color": "#444441", "width": 1.25,
"dash": { "cel": "self.identifying ? 'solid' : 'dashed'" } },
"routing": "orthogonal",
"cornerRadius": 0,
"avoidNodes": true,
"minSegmentLength": 18,
"startDirection": "normal",
"endDirection": "normal",
"bendpoints": { "editable": true }
},
"jumps": { "style": "gap", "size": 5 },
"sourceMarker": { "cel": "'er' + self.sourceCardinality.substring(0, 1).upperAscii() + self.sourceCardinality.substring(1)" },
"targetMarker": { "cel": "'er' + self.targetCardinality.substring(0, 1).upperAscii() + self.targetCardinality.substring(1)" },
"labels": [
{ "id": "name", "text": { "attribute": "name" }, "at": "middle", "side": "above", "orientation": "upright",
"style": { "font": { "style": "italic", "size": 11 } }, "editable": "inline" },
{ "id": "srcCard", "text": { "cel": "enumLabel('Cardinality', self.sourceCardinality)" }, "at": "start", "offset": 20,
"side": "below", "style": { "font": { "size": 10, "color": "#888780" } }, "visible": { "cel": "env.zoom > 1.2" } },
{ "id": "tgtCard", "text": { "cel": "enumLabel('Cardinality', self.targetCardinality)" }, "at": "end", "offset": 20,
"side": "below", "style": { "font": { "size": 10, "color": "#888780" } }, "visible": { "cel": "env.zoom > 1.2" } }
]
}
}
},
"forms": {
"columnsTable": {
"for": "Table",
"usage": ["embedded", "inspector"],
"layout": { "density": "compact" },
"items": [
{ "attribute": "name" },
{ "kind": "field", "attribute": "comment", "widget": "textarea", "rows": 2 }
]
}
},
"toolbox": {
"groups": [
{ "id": "model", "label": "Model", "tools": [
{ "id": "table", "creates": "Table", "shortcut": "T" },
{ "id": "rel", "creates": "Relationship", "shortcut": "R" },
{ "id": "rel1n", "creates": "Relationship", "variantOf": "rel", "label": "One-to-many",
"initial": { "sourceCardinality": "one", "targetCardinality": "zeroOrMany" } },
{ "id": "rel11", "creates": "Relationship", "variantOf": "rel", "label": "One-to-one",
"initial": { "sourceCardinality": "one", "targetCardinality": "zeroOrOne" } }
] }
],
"contextTools": [
{ "for": ["Table"], "tools": [
{ "kind": "create-child", "creates": "Column", "slot": "columns", "label": "Add column", "icon": "plus", "position": "bottom" }
] }
]
},
"constraints": {
"rules": [
{ "id": "hasPrimaryKey", "scope": "Table",
"rule": "self.children.exists(c, c.primaryKey)",
"severity": "warning", "message": { "cel": "'Table ' + self.name + ' has no primary key.'" },
"fixes": [ { "label": "Add an id column",
"actions": [ { "create": { "type": "'Column'", "parent": "self", "slot": "'columns'",
"attributes": { "name": "'id'", "dataType": "'uuid'", "primaryKey": "true", "nullable": "false" } } } ] } ] },
{ "id": "fkTypeMatches", "scope": "Column", "when": "has(self.references)",
"rule": "self.references.dataType == self.dataType",
"attribute": "dataType",
"message": { "cel": "'Foreign key type ' + self.dataType + ' does not match ' + self.references.parent.name + '.' + self.references.name + ' (' + self.references.dataType + ').'" },
"fixes": [ { "label": "Use the referenced column's type", "actions": [ { "set": { "dataType": "self.references.dataType" } } ] } ] },
{ "id": "pkNotNull", "scope": "Column", "when": "self.primaryKey", "rule": "!self.nullable",
"enforcement": "prevent-and-report", "attribute": "nullable",
"message": "Primary key columns cannot be nullable." }
]
},
"behavior": {
"hooks": [
{ "id": "pkImpliesNotNull", "on": "change", "for": "Column", "attribute": "primaryKey", "phase": "before",
"when": "self.primaryKey", "actions": [ { "set": { "nullable": "false" } } ] }
]
},
"layout": {
"algorithms": { "orthogonal": { "algorithm": "elk:org.eclipse.elk.layered", "direction": "right",
"edgeRouting": "orthogonal", "spacing": { "node": 60, "layer": 120 } } },
"default": "orthogonal"
},
"persistence": {
"format": "yaml",
"ids": { "strategy": "natural", "stable": true },
"ordering": { "elements": "tree" },
"view": { "store": ["bounds", "waypoints", "labelOffsets", "collapsed"] },
"export": [ { "format": "plugin:sql-ddl", "label": "SQL DDL" } ]
},
"plugins": {
"sql-ddl": { "version": "^1.0.0", "provides": ["export"], "required": false,
"doc": "Generates CREATE TABLE statements." }
}
}In an IDE
No IDE host runs this example yet.