@eventcatalog/language-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/SPEC.md +1939 -0
- package/dist/ast-utils.d.ts +96 -0
- package/dist/ast-utils.d.ts.map +1 -0
- package/dist/ast-utils.js +241 -0
- package/dist/ast-utils.js.map +1 -0
- package/dist/compiler.d.ts +7 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +654 -0
- package/dist/compiler.js.map +1 -0
- package/dist/ec-completion-provider.d.ts +15 -0
- package/dist/ec-completion-provider.d.ts.map +1 -0
- package/dist/ec-completion-provider.js +202 -0
- package/dist/ec-completion-provider.js.map +1 -0
- package/dist/ec-module.d.ts +18 -0
- package/dist/ec-module.d.ts.map +1 -0
- package/dist/ec-module.js +27 -0
- package/dist/ec-module.js.map +1 -0
- package/dist/ec-scope-provider.d.ts +2 -0
- package/dist/ec-scope-provider.d.ts.map +1 -0
- package/dist/ec-scope-provider.js +2 -0
- package/dist/ec-scope-provider.js.map +1 -0
- package/dist/ec-scope.d.ts +10 -0
- package/dist/ec-scope.d.ts.map +1 -0
- package/dist/ec-scope.js +18 -0
- package/dist/ec-scope.js.map +1 -0
- package/dist/ec-validator.d.ts +9 -0
- package/dist/ec-validator.d.ts.map +1 -0
- package/dist/ec-validator.js +238 -0
- package/dist/ec-validator.js.map +1 -0
- package/dist/formatter.d.ts +6 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +88 -0
- package/dist/formatter.js.map +1 -0
- package/dist/generated/ast.d.ts +970 -0
- package/dist/generated/ast.d.ts.map +1 -0
- package/dist/generated/ast.js +1537 -0
- package/dist/generated/ast.js.map +1 -0
- package/dist/generated/grammar.d.ts +7 -0
- package/dist/generated/grammar.d.ts.map +1 -0
- package/dist/generated/grammar.js +6062 -0
- package/dist/generated/grammar.js.map +1 -0
- package/dist/generated/module.d.ts +14 -0
- package/dist/generated/module.d.ts.map +1 -0
- package/dist/generated/module.js +21 -0
- package/dist/generated/module.js.map +1 -0
- package/dist/graph-types.d.ts +32 -0
- package/dist/graph-types.d.ts.map +1 -0
- package/dist/graph-types.js +2 -0
- package/dist/graph-types.js.map +1 -0
- package/dist/graph.d.ts +4 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +931 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/main-browser.d.ts +2 -0
- package/dist/main-browser.d.ts.map +1 -0
- package/dist/main-browser.js +16 -0
- package/dist/main-browser.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +15 -0
- package/dist/main.js.map +1 -0
- package/package.json +55 -0
- package/specification/00-overview.md +99 -0
- package/specification/01-domain.md +80 -0
- package/specification/02-service.md +50 -0
- package/specification/03-event.md +28 -0
- package/specification/04-command.md +25 -0
- package/specification/05-query.md +25 -0
- package/specification/06-channel.md +131 -0
- package/specification/08-container.md +54 -0
- package/specification/09-data-product.md +39 -0
- package/specification/10-flow.md +163 -0
- package/specification/11-diagram.md +3 -0
- package/specification/12-user.md +54 -0
- package/specification/13-team.md +62 -0
- package/specification/14-relationships.md +89 -0
- package/specification/15-versioning.md +41 -0
- package/specification/16-annotations.md +100 -0
- package/specification/17-examples.md +373 -0
- package/specification/18-grammar.md +242 -0
- package/specification/19-visualizer.md +197 -0
- package/specification/build-spec.sh +49 -0
- package/syntaxes/ec.tmLanguage.json +61 -0
package/SPEC.md
ADDED
|
@@ -0,0 +1,1939 @@
|
|
|
1
|
+
# EventCatalog DSL — Language Specification
|
|
2
|
+
|
|
3
|
+
> Version: 1.0.0-draft
|
|
4
|
+
> Status: Draft
|
|
5
|
+
> Date: 2026-02-08
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Overview](#overview)
|
|
10
|
+
2. [Domain](#domain)
|
|
11
|
+
3. [Service](#service)
|
|
12
|
+
4. [Event](#event)
|
|
13
|
+
5. [Command](#command)
|
|
14
|
+
6. [Query](#query)
|
|
15
|
+
7. [Channel](#channel)
|
|
16
|
+
8. [Container](#container)
|
|
17
|
+
9. [Data Product](#data-product)
|
|
18
|
+
10. [Flow](#flow)
|
|
19
|
+
11. [User](#user)
|
|
20
|
+
12. [Team](#team)
|
|
21
|
+
13. [Relationships & Pointers](#relationships--pointers)
|
|
22
|
+
14. [Versioning](#versioning)
|
|
23
|
+
15. [Metadata & Annotations](#metadata--annotations)
|
|
24
|
+
16. [Complete Examples](#complete-examples)
|
|
25
|
+
17. [Full Grammar (EBNF)](#full-grammar-ebnf)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# Overview
|
|
30
|
+
|
|
31
|
+
The EventCatalog DSL (ECDSL) is a human-readable, declarative language for defining event-driven architectures. It compiles to EventCatalog's frontmatter/markdown format, enabling teams to define domains, services, messages, channels, and their relationships in a single coherent source.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
domain Payment {
|
|
35
|
+
version 1.0.0
|
|
36
|
+
owner payment-team
|
|
37
|
+
|
|
38
|
+
service PaymentService {
|
|
39
|
+
version 1.0.0
|
|
40
|
+
|
|
41
|
+
sends event PaymentProcessed {
|
|
42
|
+
version 1.0.0
|
|
43
|
+
summary "Emitted when a payment completes successfully"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
receives command ProcessPayment
|
|
47
|
+
receives event OrderCreated
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Design Principles
|
|
53
|
+
|
|
54
|
+
1. **Readable** — Reads like English; minimal punctuation
|
|
55
|
+
2. **Hierarchical** — Nesting reflects domain ownership and relationships
|
|
56
|
+
3. **Concise** — Sane defaults; only specify what you need
|
|
57
|
+
4. **Complete** — Can express everything EventCatalog supports
|
|
58
|
+
5. **Composable** — Resources can be defined inline or referenced by ID
|
|
59
|
+
|
|
60
|
+
## Lexical Structure
|
|
61
|
+
|
|
62
|
+
### Comments
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
// Single-line comment
|
|
66
|
+
|
|
67
|
+
/* Multi-line
|
|
68
|
+
comment */
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Strings
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
"double-quoted string"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Strings are required for values containing spaces, special characters, or multi-word text. Bare identifiers (no spaces, alphanumeric + hyphens + dots) can be unquoted. Strings follow JSON-style escaping (`\"`, `\\`, `\n`, etc.); raw `"` inside a string is not allowed.
|
|
78
|
+
|
|
79
|
+
### Identifiers
|
|
80
|
+
|
|
81
|
+
Identifiers are bare words used for resource IDs and type names. Keywords are reserved and cannot be used as identifiers.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
OrderCreated // simple
|
|
85
|
+
Payment.OrderCreated // namespaced
|
|
86
|
+
my-service-name // kebab-case
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Version Literals
|
|
90
|
+
|
|
91
|
+
Semantic versions are bare (unquoted):
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
version 1.0.0
|
|
95
|
+
version 2.1.0-beta.1
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Blocks
|
|
99
|
+
|
|
100
|
+
Curly braces delimit blocks. Properties inside blocks are newline-separated (no commas).
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
resource Foo {
|
|
104
|
+
property value
|
|
105
|
+
property "value with spaces"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Property Multiplicity
|
|
110
|
+
|
|
111
|
+
The grammar allows any property to appear multiple times inside a block. The semantic rules are:
|
|
112
|
+
|
|
113
|
+
**Single-value properties** — if repeated, the last occurrence wins:
|
|
114
|
+
|
|
115
|
+
`version`, `name`, `summary`, `address`, `protocol`, `deprecated`, `draft`, `container-type`, `technology`, `authoritative`, `access-mode`, `classification`, `residency`, `retention`
|
|
116
|
+
|
|
117
|
+
`schema` is also single-value but only valid on messages (`event`, `command`, `query`, and inline message definitions).
|
|
118
|
+
|
|
119
|
+
**Repeatable properties** — each occurrence appends a value:
|
|
120
|
+
|
|
121
|
+
`owner`, `sends`, `receives`, `writes-to`, `reads-from`, `flow`, `service` (ref), `subdomain`, `data-product`, `route`, `input`, `output`, `member`, `parameter`
|
|
122
|
+
|
|
123
|
+
`parameter` names must be unique within a channel; duplicate names are an error.
|
|
124
|
+
|
|
125
|
+
Annotations are repeatable by default (see Metadata & Annotations).
|
|
126
|
+
|
|
127
|
+
Unknown properties are parse errors — only the properties listed in each resource's grammar are valid. Duplicate single-value properties are syntactically valid (last wins) but tooling may warn.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
# Domain
|
|
132
|
+
|
|
133
|
+
Top-level bounded context. Can contain services, subdomains, data products, and flows.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
domain <id> {
|
|
137
|
+
// Required
|
|
138
|
+
version <semver>
|
|
139
|
+
name "<display name>" // optional, defaults to id
|
|
140
|
+
|
|
141
|
+
// Optional metadata
|
|
142
|
+
summary "<text>"
|
|
143
|
+
owner <owner-ref> // repeatable
|
|
144
|
+
deprecated true
|
|
145
|
+
draft true
|
|
146
|
+
|
|
147
|
+
// Relationships
|
|
148
|
+
service <service-ref> // repeatable (reference to external service)
|
|
149
|
+
subdomain <domain-ref> // repeatable
|
|
150
|
+
data-product <dp-ref> // repeatable
|
|
151
|
+
flow <flow-ref> // repeatable
|
|
152
|
+
|
|
153
|
+
// Domain-level message routing
|
|
154
|
+
sends <message-type> <id> [to <channel-ref>]
|
|
155
|
+
receives <message-type> <id> [from <channel-ref>]
|
|
156
|
+
|
|
157
|
+
// Inline definitions
|
|
158
|
+
service <id> { ... }
|
|
159
|
+
subdomain <id> { ... }
|
|
160
|
+
|
|
161
|
+
// Annotations (see Metadata section)
|
|
162
|
+
@badge(...)
|
|
163
|
+
@repository(...)
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Subdomains
|
|
168
|
+
|
|
169
|
+
Domains can contain nested subdomains:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
domain Logistics {
|
|
173
|
+
version 1.0.0
|
|
174
|
+
|
|
175
|
+
subdomain Shipping {
|
|
176
|
+
version 1.0.0
|
|
177
|
+
summary "Package shipping and tracking"
|
|
178
|
+
|
|
179
|
+
service ShippingService {
|
|
180
|
+
version 1.0.0
|
|
181
|
+
receives event OrderCreated
|
|
182
|
+
sends event ShipmentCreated
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
subdomain Returns {
|
|
187
|
+
version 1.0.0
|
|
188
|
+
summary "Return merchandise authorization"
|
|
189
|
+
|
|
190
|
+
service ReturnsService {
|
|
191
|
+
version 1.0.0
|
|
192
|
+
receives command InitiateReturn
|
|
193
|
+
sends event ReturnApproved
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## EBNF
|
|
200
|
+
|
|
201
|
+
```ebnf
|
|
202
|
+
domain_decl = "domain" identifier "{" common_props
|
|
203
|
+
{ domain_body_item } "}" ;
|
|
204
|
+
domain_body_item = service_decl | subdomain_decl | service_ref_stmt
|
|
205
|
+
| data_product_ref_stmt | flow_ref_stmt
|
|
206
|
+
| sends_stmt | receives_stmt
|
|
207
|
+
| annotation ;
|
|
208
|
+
subdomain_decl = "subdomain" identifier "{" common_props
|
|
209
|
+
{ domain_body_item } "}" ;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
# Service
|
|
215
|
+
|
|
216
|
+
A microservice or application.
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
service <id> {
|
|
220
|
+
// Required
|
|
221
|
+
version <semver>
|
|
222
|
+
name "<display name>"
|
|
223
|
+
|
|
224
|
+
// Optional metadata
|
|
225
|
+
summary "<text>"
|
|
226
|
+
owner <owner-ref> // repeatable
|
|
227
|
+
deprecated true
|
|
228
|
+
draft true
|
|
229
|
+
|
|
230
|
+
// Message relationships
|
|
231
|
+
sends <message-type> <id>[@<version>] [to <channel-list>]
|
|
232
|
+
receives <message-type> <id>[@<version>] [from <channel-list>]
|
|
233
|
+
|
|
234
|
+
// Data relationships
|
|
235
|
+
writes-to container <container-ref> // repeatable
|
|
236
|
+
reads-from container <container-ref> // repeatable
|
|
237
|
+
|
|
238
|
+
// Flow relationships
|
|
239
|
+
flow <flow-ref> // repeatable
|
|
240
|
+
|
|
241
|
+
// Inline message definitions
|
|
242
|
+
sends event <id> { ... }
|
|
243
|
+
sends command <id> { ... }
|
|
244
|
+
receives query <id> { ... }
|
|
245
|
+
|
|
246
|
+
// Annotations
|
|
247
|
+
@badge(...)
|
|
248
|
+
@repository(...)
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Message types:** `event`, `command`, `query`
|
|
253
|
+
|
|
254
|
+
## EBNF
|
|
255
|
+
|
|
256
|
+
```ebnf
|
|
257
|
+
service_decl = "service" identifier "{" common_props
|
|
258
|
+
{ service_body_item } "}" ;
|
|
259
|
+
service_body_item= sends_stmt | receives_stmt
|
|
260
|
+
| writes_to_stmt | reads_from_stmt
|
|
261
|
+
| flow_ref_stmt
|
|
262
|
+
| annotation ;
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
# Event
|
|
268
|
+
|
|
269
|
+
A domain event — something that happened.
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
event <id> {
|
|
273
|
+
// Required
|
|
274
|
+
version <semver>
|
|
275
|
+
name "<display name>"
|
|
276
|
+
|
|
277
|
+
// Optional metadata
|
|
278
|
+
summary "<text>"
|
|
279
|
+
owner <owner-ref>
|
|
280
|
+
schema "<path>"
|
|
281
|
+
deprecated true
|
|
282
|
+
draft true
|
|
283
|
+
|
|
284
|
+
// Annotations
|
|
285
|
+
@badge(...)
|
|
286
|
+
@repository(...)
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## EBNF
|
|
291
|
+
|
|
292
|
+
```ebnf
|
|
293
|
+
event_decl = "event" identifier "{" message_props "}" ;
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
# Command
|
|
299
|
+
|
|
300
|
+
An instruction to perform an action.
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
command <id> {
|
|
304
|
+
version <semver>
|
|
305
|
+
name "<display name>"
|
|
306
|
+
summary "<text>"
|
|
307
|
+
owner <owner-ref>
|
|
308
|
+
schema "<path>"
|
|
309
|
+
deprecated true
|
|
310
|
+
draft true
|
|
311
|
+
|
|
312
|
+
channel <channel-ref>
|
|
313
|
+
|
|
314
|
+
// Same annotations as event
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## EBNF
|
|
319
|
+
|
|
320
|
+
```ebnf
|
|
321
|
+
command_decl = "command" identifier "{" message_props "}" ;
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
# Query
|
|
327
|
+
|
|
328
|
+
A request for information.
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
query <id> {
|
|
332
|
+
version <semver>
|
|
333
|
+
name "<display name>"
|
|
334
|
+
summary "<text>"
|
|
335
|
+
owner <owner-ref>
|
|
336
|
+
schema "<path>"
|
|
337
|
+
deprecated true
|
|
338
|
+
draft true
|
|
339
|
+
|
|
340
|
+
channel <channel-ref>
|
|
341
|
+
|
|
342
|
+
// Same annotations as event
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## EBNF
|
|
347
|
+
|
|
348
|
+
```ebnf
|
|
349
|
+
query_decl = "query" identifier "{" message_props "}" ;
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
# Channel
|
|
355
|
+
|
|
356
|
+
A communication channel (topic, queue, exchange, etc.).
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
channel <id> {
|
|
360
|
+
version <semver>
|
|
361
|
+
name "<display name>"
|
|
362
|
+
summary "<text>"
|
|
363
|
+
owner <owner-ref>
|
|
364
|
+
|
|
365
|
+
address "<address-string>"
|
|
366
|
+
protocol "<protocol>"
|
|
367
|
+
|
|
368
|
+
// Channel parameters
|
|
369
|
+
parameter <name> {
|
|
370
|
+
description "<text>"
|
|
371
|
+
default "<value>"
|
|
372
|
+
enum ["<val1>", "<val2>"]
|
|
373
|
+
examples ["<ex1>", "<ex2>"]
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Routing
|
|
377
|
+
route <channel-ref> // repeatable
|
|
378
|
+
|
|
379
|
+
// Annotations
|
|
380
|
+
@badge(...)
|
|
381
|
+
@repository(...)
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Channel-to-Channel Routing
|
|
386
|
+
|
|
387
|
+
Channels can route to other channels using the `route` statement. This models message pipelines where data flows through multiple channels (e.g., Kafka topic → Kafka topic → MQTT broker):
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
channel SensorIngestion {
|
|
391
|
+
version 1.0.0
|
|
392
|
+
protocol "Kafka"
|
|
393
|
+
route SensorFiltered
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
channel SensorFiltered {
|
|
397
|
+
version 1.0.0
|
|
398
|
+
protocol "Kafka"
|
|
399
|
+
route MqttDevices
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
channel MqttDevices {
|
|
403
|
+
version 1.0.0
|
|
404
|
+
protocol "MQTT"
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
This creates a chain: `SensorIngestion → SensorFiltered → MqttDevices`.
|
|
409
|
+
|
|
410
|
+
A channel can route to multiple targets (fan-out):
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
channel Ingestion {
|
|
414
|
+
version 1.0.0
|
|
415
|
+
route Analytics
|
|
416
|
+
route Archive
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Routes can include versioned references:
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
channel Source {
|
|
424
|
+
version 1.0.0
|
|
425
|
+
route Target@2.0.0
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Service-to-Channel Routing
|
|
430
|
+
|
|
431
|
+
Services send/receive messages through channels:
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
service OrderService {
|
|
435
|
+
version 1.0.0
|
|
436
|
+
|
|
437
|
+
// Simple — no channel
|
|
438
|
+
sends event OrderCreated
|
|
439
|
+
|
|
440
|
+
// To a single channel
|
|
441
|
+
sends event OrderCreated to orders-topic
|
|
442
|
+
|
|
443
|
+
// To a channel with version (using @ syntax)
|
|
444
|
+
sends event OrderCreated to orders-topic@1.0.0
|
|
445
|
+
|
|
446
|
+
// To multiple channels (comma-separated)
|
|
447
|
+
sends event OrderCreated to orders-topic, orders-backup-topic
|
|
448
|
+
|
|
449
|
+
// To multiple channels with versions
|
|
450
|
+
sends event OrderCreated to orders-topic@1.0.0, orders-backup-topic@2.0.0
|
|
451
|
+
|
|
452
|
+
// Receiving from a single channel
|
|
453
|
+
receives event PaymentProcessed from payment-events
|
|
454
|
+
|
|
455
|
+
// Receiving from multiple channels
|
|
456
|
+
receives event PaymentProcessed from payment-events, payment-retry-queue
|
|
457
|
+
|
|
458
|
+
// Receiving from channels with versions
|
|
459
|
+
receives event PaymentProcessed from payment-events@1.0.0, payment-retry-queue@2.1.0
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## EBNF
|
|
464
|
+
|
|
465
|
+
```ebnf
|
|
466
|
+
channel_decl = "channel" identifier "{" common_props
|
|
467
|
+
{ channel_body_item } "}" ;
|
|
468
|
+
channel_body_item= address_prop | protocol_prop | parameter_decl
|
|
469
|
+
| route_stmt | annotation ;
|
|
470
|
+
address_prop = "address" string_lit ;
|
|
471
|
+
protocol_prop = "protocol" string_lit ;
|
|
472
|
+
parameter_decl = "parameter" identifier "{" { param_prop } "}" ;
|
|
473
|
+
param_prop = "description" string_lit
|
|
474
|
+
| "default" string_lit
|
|
475
|
+
| "enum" "[" string_lit { "," string_lit } "]"
|
|
476
|
+
| "examples" "[" string_lit { "," string_lit } "]" ;
|
|
477
|
+
route_stmt = "route" resource_ref ;
|
|
478
|
+
|
|
479
|
+
channel_clause = to_clause | from_clause ;
|
|
480
|
+
to_clause = "to" channel_ref_list ;
|
|
481
|
+
from_clause = "from" channel_ref_list ;
|
|
482
|
+
channel_ref_list = channel_ref { "," channel_ref } ;
|
|
483
|
+
channel_ref = identifier [ "@" version_lit ] ;
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
# Container
|
|
489
|
+
|
|
490
|
+
A data store, cache, or external system.
|
|
491
|
+
|
|
492
|
+
```
|
|
493
|
+
container <id> {
|
|
494
|
+
version <semver>
|
|
495
|
+
name "<display name>"
|
|
496
|
+
summary "<text>"
|
|
497
|
+
owner <owner-ref>
|
|
498
|
+
|
|
499
|
+
// Required
|
|
500
|
+
container-type <database | cache | objectStore | searchIndex
|
|
501
|
+
| dataWarehouse | dataLake | externalSaaS | other>
|
|
502
|
+
|
|
503
|
+
// Optional
|
|
504
|
+
deprecated true
|
|
505
|
+
draft true
|
|
506
|
+
technology "<tech-string>" // e.g., "postgres@15", "redis@7"
|
|
507
|
+
authoritative true
|
|
508
|
+
access-mode <read | write | readWrite | appendOnly>
|
|
509
|
+
classification <public | internal | confidential | regulated>
|
|
510
|
+
residency "<location>"
|
|
511
|
+
retention "<duration>" // e.g., "90d", "10y"
|
|
512
|
+
|
|
513
|
+
// Relationships
|
|
514
|
+
service <service-ref> // repeatable
|
|
515
|
+
|
|
516
|
+
// Annotations
|
|
517
|
+
@badge(...)
|
|
518
|
+
@repository(...)
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## EBNF
|
|
523
|
+
|
|
524
|
+
```ebnf
|
|
525
|
+
container_decl = "container" identifier "{" common_props
|
|
526
|
+
{ container_body_item } "}" ;
|
|
527
|
+
container_body_item = container_type_prop | technology_prop
|
|
528
|
+
| authoritative_prop | access_mode_prop
|
|
529
|
+
| classification_prop | residency_prop
|
|
530
|
+
| retention_prop
|
|
531
|
+
| service_ref_stmt | annotation ;
|
|
532
|
+
container_type_prop = "container-type" container_type_enum ;
|
|
533
|
+
container_type_enum = "database" | "cache" | "objectStore" | "searchIndex"
|
|
534
|
+
| "dataWarehouse" | "dataLake" | "externalSaaS" | "other" ;
|
|
535
|
+
technology_prop = "technology" string_lit ;
|
|
536
|
+
authoritative_prop = "authoritative" bool_lit ;
|
|
537
|
+
access_mode_prop = "access-mode" ( "read" | "write" | "readWrite" | "appendOnly" ) ;
|
|
538
|
+
classification_prop = "classification" ( "public" | "internal" | "confidential" | "regulated" ) ;
|
|
539
|
+
residency_prop = "residency" string_lit ;
|
|
540
|
+
retention_prop = "retention" string_lit ;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
# Data Product
|
|
546
|
+
|
|
547
|
+
An analytical data product.
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
data-product <id> {
|
|
551
|
+
version <semver>
|
|
552
|
+
name "<display name>"
|
|
553
|
+
summary "<text>"
|
|
554
|
+
owner <owner-ref>
|
|
555
|
+
deprecated true
|
|
556
|
+
draft true
|
|
557
|
+
|
|
558
|
+
// Data lineage
|
|
559
|
+
input <message-type> <resource-ref> // repeatable
|
|
560
|
+
output <message-type> <resource-ref> { // repeatable, with optional contract
|
|
561
|
+
contract {
|
|
562
|
+
path "<path>"
|
|
563
|
+
name "<name>"
|
|
564
|
+
type "<type>"
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Annotations
|
|
569
|
+
@badge(...)
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## EBNF
|
|
574
|
+
|
|
575
|
+
```ebnf
|
|
576
|
+
data_product_decl = "data-product" identifier "{" common_props
|
|
577
|
+
{ dp_body_item } "}" ;
|
|
578
|
+
dp_body_item = input_stmt | output_stmt | annotation ;
|
|
579
|
+
input_stmt = "input" message_type resource_ref ;
|
|
580
|
+
output_stmt = "output" message_type resource_ref [ "{" contract_block "}" ] ;
|
|
581
|
+
contract_block = "contract" "{" "path" string_lit "name" string_lit
|
|
582
|
+
[ "type" string_lit ] "}" ;
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
# Flow
|
|
588
|
+
|
|
589
|
+
Flows define step-by-step business processes using a PM-friendly `when`-block syntax. Resources are referenced by name only — types are resolved from the catalog.
|
|
590
|
+
|
|
591
|
+
```
|
|
592
|
+
flow <id> {
|
|
593
|
+
version <semver>
|
|
594
|
+
name "<display name>"
|
|
595
|
+
summary "<text>"
|
|
596
|
+
owner <owner-ref>
|
|
597
|
+
|
|
598
|
+
// Entry chain — the starting sequence
|
|
599
|
+
<Name> ["<label>"] -> <Name> ["<label>"] -> ...
|
|
600
|
+
|
|
601
|
+
// When blocks — react to events
|
|
602
|
+
when <TriggerName>
|
|
603
|
+
<ServiceName> "<description>"
|
|
604
|
+
-> "<label>": <OutputName>
|
|
605
|
+
|
|
606
|
+
// Convergence — multiple triggers must complete
|
|
607
|
+
when <TriggerA> and <TriggerB>
|
|
608
|
+
<ServiceName> "<description>"
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
## Actors and External Systems
|
|
613
|
+
|
|
614
|
+
To use actors and external systems in flows, define them as top-level resources. The flow resolves their type automatically by name:
|
|
615
|
+
|
|
616
|
+
```
|
|
617
|
+
actor Customer {
|
|
618
|
+
name "Customer"
|
|
619
|
+
summary "End user on the storefront"
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
external-system WarehouseWMS {
|
|
623
|
+
name "Warehouse WMS"
|
|
624
|
+
summary "Legacy warehouse management system"
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
Both support optional bodies with `name`, `summary`, and annotations. They can also be bare (no body):
|
|
629
|
+
|
|
630
|
+
```
|
|
631
|
+
actor Customer
|
|
632
|
+
external-system WarehouseWMS
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
## Flow References
|
|
636
|
+
|
|
637
|
+
Resources in flows are referenced by name only (no type keywords like `service` or `event`). Types are resolved from catalog definitions or sibling `.ec` files. If no matching definition is found, the default type is `step`. Compilers may warn on unresolved flow references.
|
|
638
|
+
|
|
639
|
+
Each reference can include an optional label:
|
|
640
|
+
|
|
641
|
+
- `Customer "places an order"` — name with display label
|
|
642
|
+
- `PlaceOrder` — bare name (no label)
|
|
643
|
+
- `PaymentService "processes the payment"` — service with description
|
|
644
|
+
|
|
645
|
+
## Entry Chains
|
|
646
|
+
|
|
647
|
+
The entry chain defines the starting sequence of a flow. It uses arrow (`->`) syntax:
|
|
648
|
+
|
|
649
|
+
```
|
|
650
|
+
Customer "places an order"
|
|
651
|
+
-> PlaceOrder
|
|
652
|
+
-> OrderService "creates the order"
|
|
653
|
+
-> OrderCreated
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Multiple sources can converge into a chain using commas:
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
EventA, EventB -> MergingService
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## When Blocks
|
|
663
|
+
|
|
664
|
+
`when` blocks define reactions to events. Each block starts with one or more trigger names, followed by actions:
|
|
665
|
+
|
|
666
|
+
```
|
|
667
|
+
when OrderCreated
|
|
668
|
+
PaymentService "processes the payment"
|
|
669
|
+
-> "success": PaymentProcessed
|
|
670
|
+
-> "failure": PaymentFailed
|
|
671
|
+
InventoryService "reserves stock"
|
|
672
|
+
-> StockReserved
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Labeled Outputs
|
|
676
|
+
|
|
677
|
+
Action outputs can have optional labels (quoted strings followed by a colon):
|
|
678
|
+
|
|
679
|
+
```
|
|
680
|
+
-> "success": PaymentProcessed // labeled output
|
|
681
|
+
-> StockReserved // unlabeled output
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### Convergence
|
|
685
|
+
|
|
686
|
+
Use `and` to require multiple triggers before actions execute:
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
when PaymentProcessed and StockReserved
|
|
690
|
+
FulfillmentService "ships the order"
|
|
691
|
+
-> OrderShipped
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Terminal Actions
|
|
695
|
+
|
|
696
|
+
Actions without outputs are terminal steps:
|
|
697
|
+
|
|
698
|
+
```
|
|
699
|
+
when OrderShipped
|
|
700
|
+
WarehouseWMS "syncs with legacy WMS"
|
|
701
|
+
NotificationService "notifies the customer"
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
## Example
|
|
705
|
+
|
|
706
|
+
```
|
|
707
|
+
flow OrderFulfillment {
|
|
708
|
+
version 1.0.0
|
|
709
|
+
name "Order Fulfillment"
|
|
710
|
+
summary "End-to-end order processing from placement to delivery"
|
|
711
|
+
owner fulfillment-team
|
|
712
|
+
|
|
713
|
+
Customer "places an order"
|
|
714
|
+
-> PlaceOrder
|
|
715
|
+
-> OrderService "creates the order"
|
|
716
|
+
-> OrderCreated
|
|
717
|
+
|
|
718
|
+
when OrderCreated
|
|
719
|
+
PaymentService "processes the payment"
|
|
720
|
+
-> "success": PaymentProcessed
|
|
721
|
+
-> "failure": PaymentFailed
|
|
722
|
+
InventoryService "reserves stock"
|
|
723
|
+
-> StockReserved
|
|
724
|
+
|
|
725
|
+
when PaymentFailed
|
|
726
|
+
NotificationService "notifies the customer of failure"
|
|
727
|
+
|
|
728
|
+
when PaymentProcessed and StockReserved
|
|
729
|
+
FulfillmentService "ships the order"
|
|
730
|
+
-> OrderShipped
|
|
731
|
+
|
|
732
|
+
when OrderShipped
|
|
733
|
+
WarehouseWMS "syncs with legacy WMS"
|
|
734
|
+
NotificationService "notifies the customer"
|
|
735
|
+
-> CustomerNotified
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
## EBNF
|
|
740
|
+
|
|
741
|
+
```ebnf
|
|
742
|
+
flow_decl = "flow" identifier "{" common_props
|
|
743
|
+
{ flow_entry_chain | flow_when_block } "}" ;
|
|
744
|
+
flow_entry_chain = flow_ref { "," flow_ref } ( "->" flow_ref )+ ;
|
|
745
|
+
flow_when_block = "when" flow_ref { "and" flow_ref } flow_action+ ;
|
|
746
|
+
flow_action = flow_ref { flow_output } ;
|
|
747
|
+
flow_output = "->" [ string_lit ":" ] flow_ref ;
|
|
748
|
+
flow_ref = identifier [ string_lit ] ;
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
# Diagram
|
|
754
|
+
|
|
755
|
+
_Removed from V1. May be added in a future version._
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
# User
|
|
760
|
+
|
|
761
|
+
A user definition for ownership and team membership.
|
|
762
|
+
|
|
763
|
+
```
|
|
764
|
+
user <id> {
|
|
765
|
+
name "<display name>"
|
|
766
|
+
avatar "<url>"
|
|
767
|
+
role "<role>"
|
|
768
|
+
email "<email>"
|
|
769
|
+
slack "<url>"
|
|
770
|
+
ms-teams "<url>"
|
|
771
|
+
|
|
772
|
+
// Team membership
|
|
773
|
+
team <team-id>
|
|
774
|
+
|
|
775
|
+
// Ownership declarations
|
|
776
|
+
owns domain <id>
|
|
777
|
+
owns service <id>
|
|
778
|
+
owns event <id>
|
|
779
|
+
owns command <id>
|
|
780
|
+
owns query <id>
|
|
781
|
+
}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
## Example
|
|
785
|
+
|
|
786
|
+
```
|
|
787
|
+
user dboyne {
|
|
788
|
+
name "David Boyne"
|
|
789
|
+
avatar "https://avatars.githubusercontent.com/u/3268013"
|
|
790
|
+
role "Principal Engineer"
|
|
791
|
+
email "david@company.com"
|
|
792
|
+
|
|
793
|
+
owns domain Payment
|
|
794
|
+
owns service PaymentService
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
## EBNF
|
|
799
|
+
|
|
800
|
+
```ebnf
|
|
801
|
+
user_decl = "user" identifier "{" user_props "}" ;
|
|
802
|
+
user_props = "name" string_lit
|
|
803
|
+
| "avatar" string_lit
|
|
804
|
+
| "role" string_lit
|
|
805
|
+
| "email" string_lit
|
|
806
|
+
| "slack" string_lit
|
|
807
|
+
| "ms-teams" string_lit
|
|
808
|
+
| owns_stmt
|
|
809
|
+
| "team" identifier ;
|
|
810
|
+
owns_stmt = "owns" resource_type_kw identifier ;
|
|
811
|
+
resource_type_kw = "domain" | "service" | "event" | "command" | "query" ;
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
# Team
|
|
817
|
+
|
|
818
|
+
A team definition for ownership and membership.
|
|
819
|
+
|
|
820
|
+
```
|
|
821
|
+
team <id> {
|
|
822
|
+
name "<display name>"
|
|
823
|
+
summary "<text>"
|
|
824
|
+
email "<email>"
|
|
825
|
+
slack "<url>"
|
|
826
|
+
ms-teams "<url>"
|
|
827
|
+
|
|
828
|
+
// Members
|
|
829
|
+
member <user-id> // repeatable
|
|
830
|
+
|
|
831
|
+
// Ownership declarations
|
|
832
|
+
owns domain <id>
|
|
833
|
+
owns service <id>
|
|
834
|
+
owns event <id>
|
|
835
|
+
owns command <id>
|
|
836
|
+
owns query <id>
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
## Owner References
|
|
841
|
+
|
|
842
|
+
Owners are referenced by team or user ID:
|
|
843
|
+
|
|
844
|
+
```
|
|
845
|
+
service OrderService {
|
|
846
|
+
version 1.0.0
|
|
847
|
+
owner payment-team // team reference
|
|
848
|
+
owner dboyne // user reference
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
## Example
|
|
853
|
+
|
|
854
|
+
```
|
|
855
|
+
team orders-team {
|
|
856
|
+
name "Orders Team"
|
|
857
|
+
summary "Responsible for order lifecycle"
|
|
858
|
+
email "orders@company.com"
|
|
859
|
+
slack "https://company.slack.com/channels/orders"
|
|
860
|
+
|
|
861
|
+
member dboyne
|
|
862
|
+
member jane-doe
|
|
863
|
+
}
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
## EBNF
|
|
867
|
+
|
|
868
|
+
```ebnf
|
|
869
|
+
team_decl = "team" identifier "{" team_props "}" ;
|
|
870
|
+
team_props = "name" string_lit
|
|
871
|
+
| "summary" string_lit
|
|
872
|
+
| "email" string_lit
|
|
873
|
+
| "slack" string_lit
|
|
874
|
+
| "ms-teams" string_lit
|
|
875
|
+
| "member" identifier
|
|
876
|
+
| owns_stmt ;
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
# Relationships & Pointers
|
|
882
|
+
|
|
883
|
+
## Resource References
|
|
884
|
+
|
|
885
|
+
Resources can be referenced by ID alone (resolves to `latest`) or with an explicit version:
|
|
886
|
+
|
|
887
|
+
```
|
|
888
|
+
// Reference by ID only (latest version)
|
|
889
|
+
receives event OrderCreated
|
|
890
|
+
|
|
891
|
+
// Reference by ID + version using @ syntax
|
|
892
|
+
receives event OrderCreated@2.0.0
|
|
893
|
+
|
|
894
|
+
// Reference with single channel routing
|
|
895
|
+
sends event OrderCreated to orders-topic
|
|
896
|
+
|
|
897
|
+
// Reference with channel version
|
|
898
|
+
sends event OrderCreated@1.0.0 to orders-topic@1.0.0
|
|
899
|
+
|
|
900
|
+
// Reference with multiple channels
|
|
901
|
+
sends event OrderCreated to orders-topic, backup-topic
|
|
902
|
+
receives event PaymentProcessed from payment-events, payment-retry
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
## Inline vs. Reference
|
|
906
|
+
|
|
907
|
+
Resources can be defined inline (creating them) or referenced (linking to existing):
|
|
908
|
+
|
|
909
|
+
```
|
|
910
|
+
service OrderService {
|
|
911
|
+
version 1.0.0
|
|
912
|
+
|
|
913
|
+
// Inline definition — creates the event
|
|
914
|
+
sends event OrderCreated {
|
|
915
|
+
version 1.0.0
|
|
916
|
+
summary "A new order was placed"
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Reference only — links to an existing event
|
|
920
|
+
receives event PaymentProcessed
|
|
921
|
+
receives event PaymentProcessed@2.0.0
|
|
922
|
+
}
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
Inline messages do not support the `channel` statement — use `to`/`from` on the `sends`/`receives` statement for channel routing.
|
|
926
|
+
|
|
927
|
+
## Pointer Syntax Summary
|
|
928
|
+
|
|
929
|
+
| Syntax | Meaning |
|
|
930
|
+
| ----------------------------- | ----------------------------- |
|
|
931
|
+
| `<id>` | Latest version of resource |
|
|
932
|
+
| `<id>@<version>` | Specific version |
|
|
933
|
+
| `<id> to <channel>` | With single channel routing |
|
|
934
|
+
| `<id> to <channel>@<version>` | Channel with specific version |
|
|
935
|
+
| `<id> to <ch1>, <ch2>, <ch3>` | Multiple channels |
|
|
936
|
+
| `<id> from <channel>` | Received from channel |
|
|
937
|
+
| `<id> from <ch1>, <ch2>` | Received from multiple |
|
|
938
|
+
|
|
939
|
+
## Data Relationships
|
|
940
|
+
|
|
941
|
+
```
|
|
942
|
+
writes-to container <container-ref>
|
|
943
|
+
reads-from container <container-ref>
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
## EBNF
|
|
947
|
+
|
|
948
|
+
```ebnf
|
|
949
|
+
resource_ref = identifier [ "@" version_lit ] ;
|
|
950
|
+
message_type = "event" | "command" | "query" ;
|
|
951
|
+
|
|
952
|
+
sends_stmt = "sends" message_type resource_ref [ channel_clause ]
|
|
953
|
+
| "sends" message_type identifier inline_block ;
|
|
954
|
+
receives_stmt = "receives" message_type resource_ref [ channel_clause ]
|
|
955
|
+
| "receives" message_type identifier inline_block ;
|
|
956
|
+
|
|
957
|
+
channel_clause = to_clause | from_clause ;
|
|
958
|
+
to_clause = "to" channel_ref_list ;
|
|
959
|
+
from_clause = "from" channel_ref_list ;
|
|
960
|
+
channel_ref_list = channel_ref { "," channel_ref } ;
|
|
961
|
+
channel_ref = identifier [ "@" version_lit ] ;
|
|
962
|
+
|
|
963
|
+
writes_to_stmt = "writes-to" "container" resource_ref ;
|
|
964
|
+
reads_from_stmt = "reads-from" "container" resource_ref ;
|
|
965
|
+
flow_ref_stmt = "flow" resource_ref ;
|
|
966
|
+
data_product_ref_stmt = "data-product" resource_ref ;
|
|
967
|
+
|
|
968
|
+
inline_block = "{" message_props "}" ;
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
---
|
|
972
|
+
|
|
973
|
+
# Versioning
|
|
974
|
+
|
|
975
|
+
## Declaring Versions
|
|
976
|
+
|
|
977
|
+
Every versioned resource requires a version:
|
|
978
|
+
|
|
979
|
+
```
|
|
980
|
+
event OrderCreated {
|
|
981
|
+
version 1.0.0
|
|
982
|
+
}
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
## Referencing Versions
|
|
986
|
+
|
|
987
|
+
```
|
|
988
|
+
// Latest (default - no version specified)
|
|
989
|
+
receives event OrderCreated
|
|
990
|
+
|
|
991
|
+
// Specific version using @ syntax
|
|
992
|
+
receives event OrderCreated@2.0.0
|
|
993
|
+
|
|
994
|
+
// Channel with version
|
|
995
|
+
sends event OrderProcessed to payments-channel@1.0.0
|
|
996
|
+
|
|
997
|
+
// Multiple channels with versions
|
|
998
|
+
receives event PaymentFailed from channel-a@1.0.0, channel-b@2.1.0
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
## Version Defaults
|
|
1002
|
+
|
|
1003
|
+
When no version is specified in a reference, it resolves to `latest`.
|
|
1004
|
+
|
|
1005
|
+
## EBNF
|
|
1006
|
+
|
|
1007
|
+
```ebnf
|
|
1008
|
+
int = digit { digit } ;
|
|
1009
|
+
version_lit = int "." int "." int [ "-" prerelease ] ;
|
|
1010
|
+
version_prop = "version" version_lit ;
|
|
1011
|
+
version_ref = "@" version_lit ;
|
|
1012
|
+
resource_ref = identifier [ version_ref ] ;
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
---
|
|
1016
|
+
|
|
1017
|
+
# Metadata & Annotations
|
|
1018
|
+
|
|
1019
|
+
Annotations use the `@` prefix and provide additional metadata. They can appear inside any resource block. All annotations are repeatable by default — multiple instances of the same annotation may appear on a single resource.
|
|
1020
|
+
|
|
1021
|
+
## @badge
|
|
1022
|
+
|
|
1023
|
+
```
|
|
1024
|
+
@badge("Production", bg: "#22c55e", text: "#fff")
|
|
1025
|
+
@badge("Beta", bg: "#f59e0b", text: "#000", icon: "flask")
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
## @repository
|
|
1029
|
+
|
|
1030
|
+
```
|
|
1031
|
+
@repository(url: "https://github.com/org/repo", language: "TypeScript")
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
## @editUrl
|
|
1035
|
+
|
|
1036
|
+
```
|
|
1037
|
+
@editUrl("https://github.com/org/repo/edit/main/docs/orders.md")
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
## @note
|
|
1041
|
+
|
|
1042
|
+
Adds free-form developer notes or reminders to any resource. Notes are repeatable — multiple `@note` annotations can appear on a single resource. Supported on services, events, commands, queries, channels, and inline message definitions.
|
|
1043
|
+
|
|
1044
|
+
```
|
|
1045
|
+
// Simple note
|
|
1046
|
+
@note("Come back later — needs review")
|
|
1047
|
+
|
|
1048
|
+
// With optional parameters
|
|
1049
|
+
@note("Align schema with PaymentService team", author: "dboyne", priority: "high")
|
|
1050
|
+
|
|
1051
|
+
// Multiple notes on a service
|
|
1052
|
+
service OrderService {
|
|
1053
|
+
version 1.0.0
|
|
1054
|
+
@note("TODO: add retry logic")
|
|
1055
|
+
@note("Waiting on team alignment", priority: "medium")
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Notes on events, commands, and queries
|
|
1059
|
+
event OrderCreated {
|
|
1060
|
+
version 1.0.0
|
|
1061
|
+
@note("Schema v2 adds shippingAddress field", author: "alice")
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
command CreateOrder {
|
|
1065
|
+
version 1.0.0
|
|
1066
|
+
@note("Validate idempotency key", author: "bob", priority: "high")
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Notes on channels
|
|
1070
|
+
channel OrderEvents {
|
|
1071
|
+
version 1.0.0
|
|
1072
|
+
address "orders.events"
|
|
1073
|
+
protocol "kafka"
|
|
1074
|
+
@note("Partition key is orderId", author: "infra-team")
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// Notes on inline messages (inside sends/receives)
|
|
1078
|
+
service PaymentService {
|
|
1079
|
+
version 1.0.0
|
|
1080
|
+
sends event PaymentProcessed to PaymentEvents {
|
|
1081
|
+
version 1.0.0
|
|
1082
|
+
@note("Includes refund reference when applicable", author: "dave")
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
## @detailsPanel
|
|
1088
|
+
|
|
1089
|
+
Controls visibility of detail panel sections:
|
|
1090
|
+
|
|
1091
|
+
```
|
|
1092
|
+
@detailsPanel {
|
|
1093
|
+
owners visible
|
|
1094
|
+
versions visible
|
|
1095
|
+
changelog hidden
|
|
1096
|
+
producers visible
|
|
1097
|
+
consumers hidden
|
|
1098
|
+
channels visible
|
|
1099
|
+
repository hidden
|
|
1100
|
+
specifications hidden
|
|
1101
|
+
messages hidden
|
|
1102
|
+
domains hidden
|
|
1103
|
+
services hidden
|
|
1104
|
+
containers hidden
|
|
1105
|
+
}
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
## EBNF
|
|
1109
|
+
|
|
1110
|
+
```ebnf
|
|
1111
|
+
annotation = "@" ann_name [ "(" ann_args ")" ] [ ann_block ] ;
|
|
1112
|
+
ann_name = identifier ;
|
|
1113
|
+
ann_args = ann_arg { "," ann_arg } ;
|
|
1114
|
+
ann_arg = [ identifier ":" ] ( string_lit | bool_lit | number_lit | identifier ) ;
|
|
1115
|
+
ann_block = "{" { ann_body_item } "}" ;
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
# Complete Examples
|
|
1121
|
+
|
|
1122
|
+
## Example 1: E-Commerce Platform
|
|
1123
|
+
|
|
1124
|
+
```
|
|
1125
|
+
// ============================================================
|
|
1126
|
+
// Teams & Users
|
|
1127
|
+
// ============================================================
|
|
1128
|
+
|
|
1129
|
+
user dboyne {
|
|
1130
|
+
name "David Boyne"
|
|
1131
|
+
avatar "https://avatars.githubusercontent.com/u/3268013"
|
|
1132
|
+
role "Principal Engineer"
|
|
1133
|
+
email "david@company.com"
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
user jane-doe {
|
|
1137
|
+
name "Jane Doe"
|
|
1138
|
+
avatar "https://avatars.githubusercontent.com/u/12345"
|
|
1139
|
+
role "Staff Engineer"
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
team orders-team {
|
|
1143
|
+
name "Orders Team"
|
|
1144
|
+
summary "Responsible for order lifecycle"
|
|
1145
|
+
email "orders@company.com"
|
|
1146
|
+
slack "https://company.slack.com/channels/orders"
|
|
1147
|
+
|
|
1148
|
+
member dboyne
|
|
1149
|
+
member jane-doe
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
team payment-team {
|
|
1153
|
+
name "Payment Team"
|
|
1154
|
+
summary "Handles payment processing and fraud detection"
|
|
1155
|
+
email "payments@company.com"
|
|
1156
|
+
|
|
1157
|
+
member jane-doe
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// ============================================================
|
|
1161
|
+
// Channels
|
|
1162
|
+
// ============================================================
|
|
1163
|
+
|
|
1164
|
+
channel orders-topic {
|
|
1165
|
+
version 1.0.0
|
|
1166
|
+
name "Orders Topic"
|
|
1167
|
+
summary "Kafka topic for all order-related events"
|
|
1168
|
+
address "kafka://production/orders"
|
|
1169
|
+
protocol "Kafka"
|
|
1170
|
+
|
|
1171
|
+
parameter environment {
|
|
1172
|
+
description "Deployment environment"
|
|
1173
|
+
default "production"
|
|
1174
|
+
enum ["production", "staging", "development"]
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
channel payment-queue {
|
|
1180
|
+
version 1.0.0
|
|
1181
|
+
name "Payment Queue"
|
|
1182
|
+
summary "SQS queue for payment commands"
|
|
1183
|
+
address "sqs://us-east-1/payment-processing"
|
|
1184
|
+
protocol "SQS"
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// ============================================================
|
|
1188
|
+
// Containers
|
|
1189
|
+
// ============================================================
|
|
1190
|
+
|
|
1191
|
+
container orders-db {
|
|
1192
|
+
version 1.0.0
|
|
1193
|
+
name "Orders Database"
|
|
1194
|
+
summary "Primary datastore for order data"
|
|
1195
|
+
owner orders-team
|
|
1196
|
+
|
|
1197
|
+
container-type database
|
|
1198
|
+
technology "postgres@15"
|
|
1199
|
+
authoritative true
|
|
1200
|
+
access-mode readWrite
|
|
1201
|
+
classification confidential
|
|
1202
|
+
residency "us-east-1"
|
|
1203
|
+
retention "7y"
|
|
1204
|
+
|
|
1205
|
+
@repository(url: "https://github.com/company/orders-db")
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
container orders-cache {
|
|
1209
|
+
version 1.0.0
|
|
1210
|
+
name "Orders Cache"
|
|
1211
|
+
summary "Redis cache for hot order lookups"
|
|
1212
|
+
|
|
1213
|
+
container-type cache
|
|
1214
|
+
technology "redis@7"
|
|
1215
|
+
access-mode readWrite
|
|
1216
|
+
retention "24h"
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// ============================================================
|
|
1220
|
+
// Domain
|
|
1221
|
+
// ============================================================
|
|
1222
|
+
|
|
1223
|
+
domain Orders {
|
|
1224
|
+
version 1.0.0
|
|
1225
|
+
name "Orders Domain"
|
|
1226
|
+
summary "Everything related to order management"
|
|
1227
|
+
owner orders-team
|
|
1228
|
+
|
|
1229
|
+
@badge("Core", bg: "#3b82f6", text: "#fff")
|
|
1230
|
+
@repository(url: "https://github.com/company/orders-domain")
|
|
1231
|
+
|
|
1232
|
+
service OrderService {
|
|
1233
|
+
version 1.0.0
|
|
1234
|
+
name "Order Service"
|
|
1235
|
+
summary "Manages the order lifecycle"
|
|
1236
|
+
owner orders-team
|
|
1237
|
+
|
|
1238
|
+
@repository(url: "https://github.com/company/order-service", language: "TypeScript")
|
|
1239
|
+
@badge("Production", bg: "#22c55e", text: "#fff")
|
|
1240
|
+
|
|
1241
|
+
sends event OrderCreated {
|
|
1242
|
+
version 1.0.0
|
|
1243
|
+
summary "Emitted when a new order is placed"
|
|
1244
|
+
schema "./schemas/order-created.avro"
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
sends event OrderUpdated {
|
|
1248
|
+
version 1.0.0
|
|
1249
|
+
summary "Emitted when order details change"
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
sends command ProcessPayment to payment-queue {
|
|
1253
|
+
version 1.0.0
|
|
1254
|
+
summary "Triggers payment processing for an order"
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
receives event PaymentProcessed from payment-queue
|
|
1258
|
+
receives event InventoryReserved
|
|
1259
|
+
|
|
1260
|
+
writes-to container orders-db
|
|
1261
|
+
reads-from container orders-db
|
|
1262
|
+
writes-to container orders-cache
|
|
1263
|
+
reads-from container orders-cache
|
|
1264
|
+
|
|
1265
|
+
flow OrderFulfillment@1.0.0
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
service NotificationService {
|
|
1269
|
+
version 1.0.0
|
|
1270
|
+
name "Notification Service"
|
|
1271
|
+
summary "Sends email and push notifications for order updates"
|
|
1272
|
+
|
|
1273
|
+
receives event OrderCreated
|
|
1274
|
+
receives event OrderUpdated
|
|
1275
|
+
|
|
1276
|
+
sends command SendEmail {
|
|
1277
|
+
version 1.0.0
|
|
1278
|
+
summary "Dispatches an email notification"
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
domain Payment {
|
|
1284
|
+
version 1.0.0
|
|
1285
|
+
name "Payment Domain"
|
|
1286
|
+
summary "Payment processing and fraud detection"
|
|
1287
|
+
owner payment-team
|
|
1288
|
+
|
|
1289
|
+
service PaymentService {
|
|
1290
|
+
version 1.0.0
|
|
1291
|
+
name "Payment Service"
|
|
1292
|
+
summary "Processes payments via Stripe"
|
|
1293
|
+
owner payment-team
|
|
1294
|
+
|
|
1295
|
+
@repository(url: "https://github.com/company/payment-service", language: "Go")
|
|
1296
|
+
|
|
1297
|
+
receives command ProcessPayment from payment-queue
|
|
1298
|
+
|
|
1299
|
+
sends event PaymentProcessed {
|
|
1300
|
+
version 1.0.0
|
|
1301
|
+
summary "Payment completed successfully"
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
sends event PaymentFailed {
|
|
1305
|
+
version 1.0.0
|
|
1306
|
+
summary "Payment was declined or errored"
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// ============================================================
|
|
1312
|
+
// Standalone events (defined outside services)
|
|
1313
|
+
// ============================================================
|
|
1314
|
+
|
|
1315
|
+
event InventoryReserved {
|
|
1316
|
+
version 1.0.0
|
|
1317
|
+
name "Inventory Reserved"
|
|
1318
|
+
summary "Stock has been reserved for an order"
|
|
1319
|
+
owner orders-team
|
|
1320
|
+
|
|
1321
|
+
@badge("Critical", bg: "#ef4444", text: "#fff")
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// ============================================================
|
|
1325
|
+
// Data Product
|
|
1326
|
+
// ============================================================
|
|
1327
|
+
|
|
1328
|
+
data-product OrderAnalytics {
|
|
1329
|
+
version 1.0.0
|
|
1330
|
+
name "Order Analytics"
|
|
1331
|
+
summary "Real-time and batch analytics for order metrics"
|
|
1332
|
+
owner orders-team
|
|
1333
|
+
|
|
1334
|
+
input event OrderCreated@1.0.0
|
|
1335
|
+
input event PaymentProcessed@1.0.0
|
|
1336
|
+
input event InventoryReserved@1.0.0
|
|
1337
|
+
|
|
1338
|
+
output event OrderMetrics {
|
|
1339
|
+
contract {
|
|
1340
|
+
path "./contracts/order-metrics.json"
|
|
1341
|
+
name "Order Metrics Schema"
|
|
1342
|
+
type "json-schema"
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// ============================================================
|
|
1348
|
+
// Actors & External Systems
|
|
1349
|
+
// ============================================================
|
|
1350
|
+
|
|
1351
|
+
actor Customer {
|
|
1352
|
+
name "Customer"
|
|
1353
|
+
summary "End user on the storefront"
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
external-system WarehouseWMS {
|
|
1357
|
+
name "Warehouse WMS"
|
|
1358
|
+
summary "Legacy warehouse management system via SOAP API"
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// ============================================================
|
|
1362
|
+
// Flow
|
|
1363
|
+
// ============================================================
|
|
1364
|
+
|
|
1365
|
+
flow OrderFulfillment {
|
|
1366
|
+
version 1.0.0
|
|
1367
|
+
name "Order Fulfillment"
|
|
1368
|
+
summary "End-to-end order processing from placement to delivery"
|
|
1369
|
+
owner orders-team
|
|
1370
|
+
|
|
1371
|
+
Customer "End user on the storefront"
|
|
1372
|
+
-> PlaceOrder
|
|
1373
|
+
-> OrderService
|
|
1374
|
+
-> OrderCreated
|
|
1375
|
+
|
|
1376
|
+
when OrderCreated
|
|
1377
|
+
PaymentService "processes the payment"
|
|
1378
|
+
-> "success": PaymentProcessed
|
|
1379
|
+
-> "failure": PaymentFailed
|
|
1380
|
+
InventoryService "reserves inventory"
|
|
1381
|
+
-> InventoryReserved
|
|
1382
|
+
|
|
1383
|
+
when InventoryReserved
|
|
1384
|
+
WarehouseWMS "Legacy WMS via SOAP API"
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
```
|
|
1388
|
+
|
|
1389
|
+
## Example 2: Minimal Service Definition
|
|
1390
|
+
|
|
1391
|
+
The DSL supports minimal definitions where defaults are sufficient:
|
|
1392
|
+
|
|
1393
|
+
```
|
|
1394
|
+
service OrderService {
|
|
1395
|
+
version 1.0.0
|
|
1396
|
+
sends event OrderCreated
|
|
1397
|
+
receives command ProcessPayment
|
|
1398
|
+
receives event PaymentProcessed
|
|
1399
|
+
}
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
## Example 3: Multi-Channel Routing
|
|
1403
|
+
|
|
1404
|
+
```
|
|
1405
|
+
service EventRouter {
|
|
1406
|
+
version 1.0.0
|
|
1407
|
+
summary "Routes events across multiple channels"
|
|
1408
|
+
|
|
1409
|
+
// Send to multiple channels (comma-separated)
|
|
1410
|
+
sends event OrderCreated to orders-topic, orders-archive-topic
|
|
1411
|
+
|
|
1412
|
+
// Receive from multiple channels
|
|
1413
|
+
receives event PaymentProcessed from payment-events, payment-retry-queue
|
|
1414
|
+
}
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
## Example 4: Channel Routing (IoT Pipeline)
|
|
1418
|
+
|
|
1419
|
+
```
|
|
1420
|
+
channel SensorIngestion {
|
|
1421
|
+
version 1.0.0
|
|
1422
|
+
address "sensors.raw"
|
|
1423
|
+
protocol "Kafka"
|
|
1424
|
+
summary "Raw sensor data ingestion"
|
|
1425
|
+
route SensorFiltered
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
channel SensorFiltered {
|
|
1429
|
+
version 1.0.0
|
|
1430
|
+
address "sensors.filtered"
|
|
1431
|
+
protocol "Kafka"
|
|
1432
|
+
summary "Validated sensor data"
|
|
1433
|
+
route DeviceCommands
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
channel DeviceCommands {
|
|
1437
|
+
version 1.0.0
|
|
1438
|
+
address "devices/+/commands"
|
|
1439
|
+
protocol "MQTT"
|
|
1440
|
+
summary "MQTT topic for device commands"
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
service SensorGateway {
|
|
1444
|
+
version 1.0.0
|
|
1445
|
+
summary "Ingests raw sensor readings"
|
|
1446
|
+
sends event SensorReading to SensorIngestion
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
service FilterService {
|
|
1450
|
+
version 1.0.0
|
|
1451
|
+
summary "Validates and filters sensor data"
|
|
1452
|
+
receives event SensorReading from SensorIngestion
|
|
1453
|
+
sends event DeviceAlert to SensorFiltered
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
service DeviceBridge {
|
|
1457
|
+
version 1.0.0
|
|
1458
|
+
summary "Bridges Kafka to MQTT"
|
|
1459
|
+
receives event DeviceAlert from SensorFiltered
|
|
1460
|
+
sends command RecalibrateDevice to DeviceCommands
|
|
1461
|
+
}
|
|
1462
|
+
```
|
|
1463
|
+
|
|
1464
|
+
## Example 5: Subdomains
|
|
1465
|
+
|
|
1466
|
+
```
|
|
1467
|
+
domain Logistics {
|
|
1468
|
+
version 1.0.0
|
|
1469
|
+
|
|
1470
|
+
subdomain Shipping {
|
|
1471
|
+
version 1.0.0
|
|
1472
|
+
summary "Package shipping and tracking"
|
|
1473
|
+
|
|
1474
|
+
service ShippingService {
|
|
1475
|
+
version 1.0.0
|
|
1476
|
+
receives event OrderCreated
|
|
1477
|
+
sends event ShipmentCreated
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
subdomain Returns {
|
|
1482
|
+
version 1.0.0
|
|
1483
|
+
summary "Return merchandise authorization"
|
|
1484
|
+
|
|
1485
|
+
service ReturnsService {
|
|
1486
|
+
version 1.0.0
|
|
1487
|
+
receives command InitiateReturn
|
|
1488
|
+
sends event ReturnApproved
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
```
|
|
1493
|
+
|
|
1494
|
+
---
|
|
1495
|
+
|
|
1496
|
+
# Full Grammar (EBNF)
|
|
1497
|
+
|
|
1498
|
+
```ebnf
|
|
1499
|
+
(* Top-level *)
|
|
1500
|
+
program = { top_level_decl } ;
|
|
1501
|
+
top_level_decl = domain_decl | service_decl | event_decl | command_decl
|
|
1502
|
+
| query_decl | channel_decl | container_decl
|
|
1503
|
+
| data_product_decl | flow_decl
|
|
1504
|
+
| user_decl | team_decl | visualizer_decl
|
|
1505
|
+
| actor_decl | external_system_decl ;
|
|
1506
|
+
|
|
1507
|
+
(* Identifiers and literals *)
|
|
1508
|
+
identifier = letter { letter | digit | "-" | "." | "_" } ;
|
|
1509
|
+
int = digit { digit } ;
|
|
1510
|
+
version_lit = int "." int "." int [ "-" prerelease ] ;
|
|
1511
|
+
string_lit = '"' { any_char } '"' ;
|
|
1512
|
+
bool_lit = "true" | "false" ;
|
|
1513
|
+
number_lit = digit { digit } ;
|
|
1514
|
+
|
|
1515
|
+
(* Common properties *)
|
|
1516
|
+
common_props = { version_prop | name_prop | summary_prop | owner_prop
|
|
1517
|
+
| deprecated_prop | draft_prop | annotation } ;
|
|
1518
|
+
message_props = { version_prop | name_prop | summary_prop | owner_prop
|
|
1519
|
+
| schema_prop | deprecated_prop | draft_prop | annotation } ;
|
|
1520
|
+
version_prop = "version" version_lit ;
|
|
1521
|
+
name_prop = "name" string_lit ;
|
|
1522
|
+
summary_prop = "summary" string_lit ;
|
|
1523
|
+
owner_prop = "owner" identifier ;
|
|
1524
|
+
schema_prop = "schema" string_lit ;
|
|
1525
|
+
deprecated_prop = "deprecated" bool_lit ;
|
|
1526
|
+
draft_prop = "draft" bool_lit ;
|
|
1527
|
+
|
|
1528
|
+
(* Annotations *)
|
|
1529
|
+
annotation = "@" ann_name [ "(" ann_args ")" ] [ ann_block ] ;
|
|
1530
|
+
ann_name = identifier ;
|
|
1531
|
+
ann_args = ann_arg { "," ann_arg } ;
|
|
1532
|
+
ann_arg = [ identifier ":" ] ( string_lit | bool_lit | number_lit | identifier ) ;
|
|
1533
|
+
ann_block = "{" { ann_body_item } "}" ;
|
|
1534
|
+
|
|
1535
|
+
(* Resource references *)
|
|
1536
|
+
resource_ref = identifier [ "@" version_lit ] ;
|
|
1537
|
+
message_type = "event" | "command" | "query" ;
|
|
1538
|
+
|
|
1539
|
+
(* Domain *)
|
|
1540
|
+
domain_decl = "domain" identifier "{" common_props
|
|
1541
|
+
{ domain_body_item } "}" ;
|
|
1542
|
+
domain_body_item = service_decl | subdomain_decl
|
|
1543
|
+
| data_product_ref_stmt | flow_ref_stmt
|
|
1544
|
+
| sends_stmt | receives_stmt
|
|
1545
|
+
| annotation ;
|
|
1546
|
+
subdomain_decl = "subdomain" identifier "{" common_props
|
|
1547
|
+
{ domain_body_item } "}" ;
|
|
1548
|
+
|
|
1549
|
+
(* Service *)
|
|
1550
|
+
service_decl = "service" identifier "{" common_props
|
|
1551
|
+
{ service_body_item } "}" ;
|
|
1552
|
+
service_body_item= sends_stmt | receives_stmt
|
|
1553
|
+
| writes_to_stmt | reads_from_stmt
|
|
1554
|
+
| flow_ref_stmt
|
|
1555
|
+
| annotation ;
|
|
1556
|
+
|
|
1557
|
+
(* Sends / Receives *)
|
|
1558
|
+
sends_stmt = "sends" message_type resource_ref [ channel_clause ]
|
|
1559
|
+
| "sends" message_type identifier inline_block ;
|
|
1560
|
+
receives_stmt = "receives" message_type resource_ref [ channel_clause ]
|
|
1561
|
+
| "receives" message_type identifier inline_block ;
|
|
1562
|
+
|
|
1563
|
+
channel_clause = to_clause | from_clause ;
|
|
1564
|
+
to_clause = "to" channel_ref_list ;
|
|
1565
|
+
from_clause = "from" channel_ref_list ;
|
|
1566
|
+
channel_ref_list = channel_ref { "," channel_ref } ;
|
|
1567
|
+
channel_ref = identifier [ "@" version_lit ] ;
|
|
1568
|
+
|
|
1569
|
+
(* Data relationships *)
|
|
1570
|
+
writes_to_stmt = "writes-to" "container" resource_ref ;
|
|
1571
|
+
reads_from_stmt = "reads-from" "container" resource_ref ;
|
|
1572
|
+
flow_ref_stmt = "flow" resource_ref ;
|
|
1573
|
+
data_product_ref_stmt = "data-product" resource_ref ;
|
|
1574
|
+
|
|
1575
|
+
(* Inline message block *)
|
|
1576
|
+
inline_block = "{" message_props "}" ;
|
|
1577
|
+
|
|
1578
|
+
(* Messages: Event, Command, Query *)
|
|
1579
|
+
event_decl = "event" identifier "{" message_props "}" ;
|
|
1580
|
+
command_decl = "command" identifier "{" message_props "}" ;
|
|
1581
|
+
query_decl = "query" identifier "{" message_props "}" ;
|
|
1582
|
+
|
|
1583
|
+
(* Channel *)
|
|
1584
|
+
channel_decl = "channel" identifier "{" common_props
|
|
1585
|
+
{ channel_body_item } "}" ;
|
|
1586
|
+
channel_body_item= address_prop | protocol_prop | parameter_decl
|
|
1587
|
+
| route_stmt | annotation ;
|
|
1588
|
+
address_prop = "address" string_lit ;
|
|
1589
|
+
protocol_prop = "protocol" string_lit ;
|
|
1590
|
+
parameter_decl = "parameter" identifier "{" { param_prop } "}" ;
|
|
1591
|
+
param_prop = "description" string_lit
|
|
1592
|
+
| "default" string_lit
|
|
1593
|
+
| "enum" "[" string_lit { "," string_lit } "]"
|
|
1594
|
+
| "examples" "[" string_lit { "," string_lit } "]" ;
|
|
1595
|
+
route_stmt = "route" resource_ref ;
|
|
1596
|
+
|
|
1597
|
+
(* Container *)
|
|
1598
|
+
container_decl = "container" identifier "{" common_props
|
|
1599
|
+
{ container_body_item } "}" ;
|
|
1600
|
+
container_body_item = container_type_prop | technology_prop
|
|
1601
|
+
| authoritative_prop | access_mode_prop
|
|
1602
|
+
| classification_prop | residency_prop
|
|
1603
|
+
| retention_prop
|
|
1604
|
+
| service_ref_stmt | annotation ;
|
|
1605
|
+
container_type_prop = "container-type" container_type_enum ;
|
|
1606
|
+
container_type_enum = "database" | "cache" | "objectStore" | "searchIndex"
|
|
1607
|
+
| "dataWarehouse" | "dataLake" | "externalSaaS" | "other" ;
|
|
1608
|
+
technology_prop = "technology" string_lit ;
|
|
1609
|
+
authoritative_prop = "authoritative" bool_lit ;
|
|
1610
|
+
access_mode_prop = "access-mode" ( "read" | "write" | "readWrite" | "appendOnly" ) ;
|
|
1611
|
+
classification_prop = "classification" ( "public" | "internal" | "confidential" | "regulated" ) ;
|
|
1612
|
+
residency_prop = "residency" string_lit ;
|
|
1613
|
+
retention_prop = "retention" string_lit ;
|
|
1614
|
+
|
|
1615
|
+
(* Data Product *)
|
|
1616
|
+
data_product_decl = "data-product" identifier "{" common_props
|
|
1617
|
+
{ dp_body_item } "}" ;
|
|
1618
|
+
dp_body_item = input_stmt | output_stmt | annotation ;
|
|
1619
|
+
input_stmt = "input" message_type resource_ref ;
|
|
1620
|
+
output_stmt = "output" message_type resource_ref [ "{" contract_block "}" ] ;
|
|
1621
|
+
contract_block = "contract" "{" "path" string_lit "name" string_lit
|
|
1622
|
+
[ "type" string_lit ] "}" ;
|
|
1623
|
+
|
|
1624
|
+
(* Flow *)
|
|
1625
|
+
flow_decl = "flow" identifier "{" common_props
|
|
1626
|
+
{ flow_entry_chain | flow_when_block } "}" ;
|
|
1627
|
+
flow_entry_chain = flow_ref { "," flow_ref } ( "->" flow_ref )+ ;
|
|
1628
|
+
flow_when_block = "when" flow_ref { "and" flow_ref } flow_action+ ;
|
|
1629
|
+
flow_action = flow_ref { flow_output } ;
|
|
1630
|
+
flow_output = "->" [ string_lit ":" ] flow_ref ;
|
|
1631
|
+
flow_ref = identifier [ string_lit ] ;
|
|
1632
|
+
|
|
1633
|
+
(* Actor *)
|
|
1634
|
+
actor_decl = "actor" identifier [ "{" { actor_body_item } "}" ] ;
|
|
1635
|
+
actor_body_item = name_prop | summary_prop | annotation ;
|
|
1636
|
+
|
|
1637
|
+
(* External System *)
|
|
1638
|
+
external_system_decl = "external-system" identifier [ "{" { ext_sys_body_item } "}" ] ;
|
|
1639
|
+
ext_sys_body_item = name_prop | summary_prop | annotation ;
|
|
1640
|
+
|
|
1641
|
+
(* Visualizer *)
|
|
1642
|
+
visualizer_decl = "visualizer" identifier "{" { visualizer_body } "}" ;
|
|
1643
|
+
visualizer_body = name_prop | summary_prop | annotation
|
|
1644
|
+
| legend_prop | search_prop | toolbar_prop
|
|
1645
|
+
| focus_mode_prop | animated_prop | style_prop
|
|
1646
|
+
| domain_decl | service_decl | event_decl | command_decl
|
|
1647
|
+
| query_decl | channel_decl | container_decl
|
|
1648
|
+
| data_product_decl | flow_decl
|
|
1649
|
+
| actor_decl | external_system_decl
|
|
1650
|
+
| service_ref_stmt | domain_ref_stmt
|
|
1651
|
+
| event_ref_stmt | command_ref_stmt | query_ref_stmt
|
|
1652
|
+
| channel_ref_stmt | data_product_ref_stmt | flow_ref_stmt
|
|
1653
|
+
| container_ref_stmt ;
|
|
1654
|
+
legend_prop = "legend" bool_lit ;
|
|
1655
|
+
search_prop = "search" bool_lit ;
|
|
1656
|
+
toolbar_prop = "toolbar" bool_lit ;
|
|
1657
|
+
focus_mode_prop = "focus-mode" bool_lit ;
|
|
1658
|
+
animated_prop = "animated" bool_lit ;
|
|
1659
|
+
style_prop = "style" ( "default" | "post-it" ) ;
|
|
1660
|
+
|
|
1661
|
+
(* User *)
|
|
1662
|
+
user_decl = "user" identifier "{" user_props "}" ;
|
|
1663
|
+
user_props = "name" string_lit
|
|
1664
|
+
| "avatar" string_lit
|
|
1665
|
+
| "role" string_lit
|
|
1666
|
+
| "email" string_lit
|
|
1667
|
+
| "slack" string_lit
|
|
1668
|
+
| "ms-teams" string_lit
|
|
1669
|
+
| owns_stmt
|
|
1670
|
+
| "team" identifier ;
|
|
1671
|
+
owns_stmt = "owns" resource_type_kw identifier ;
|
|
1672
|
+
resource_type_kw = "domain" | "service" | "event" | "command" | "query" ;
|
|
1673
|
+
|
|
1674
|
+
(* Team *)
|
|
1675
|
+
team_decl = "team" identifier "{" team_props "}" ;
|
|
1676
|
+
team_props = "name" string_lit
|
|
1677
|
+
| "summary" string_lit
|
|
1678
|
+
| "email" string_lit
|
|
1679
|
+
| "slack" string_lit
|
|
1680
|
+
| "ms-teams" string_lit
|
|
1681
|
+
| "member" identifier
|
|
1682
|
+
| owns_stmt ;
|
|
1683
|
+
|
|
1684
|
+
(* Resource references *)
|
|
1685
|
+
service_ref_stmt = "service" resource_ref ;
|
|
1686
|
+
domain_ref_stmt = "domain" resource_ref ;
|
|
1687
|
+
event_ref_stmt = "event" resource_ref ;
|
|
1688
|
+
command_ref_stmt = "command" resource_ref ;
|
|
1689
|
+
query_ref_stmt = "query" resource_ref ;
|
|
1690
|
+
channel_ref_stmt = "channel" resource_ref ;
|
|
1691
|
+
container_ref_stmt = "container" resource_ref ;
|
|
1692
|
+
```
|
|
1693
|
+
|
|
1694
|
+
## Reserved Keywords
|
|
1695
|
+
|
|
1696
|
+
```
|
|
1697
|
+
domain service event command query
|
|
1698
|
+
channel container data-product flow
|
|
1699
|
+
user team sends receives
|
|
1700
|
+
writes-to reads-from owns to from
|
|
1701
|
+
version name summary owner schema
|
|
1702
|
+
deprecated draft true false
|
|
1703
|
+
type actor external-system
|
|
1704
|
+
parameter route member
|
|
1705
|
+
input output contract
|
|
1706
|
+
subdomain visualizer legend search toolbar focus-mode
|
|
1707
|
+
animated style when and
|
|
1708
|
+
```
|
|
1709
|
+
|
|
1710
|
+
## File Extension
|
|
1711
|
+
|
|
1712
|
+
EventCatalog DSL files use the `.ec` extension:
|
|
1713
|
+
|
|
1714
|
+
```
|
|
1715
|
+
catalog.ec
|
|
1716
|
+
orders-domain.ec
|
|
1717
|
+
payment-service.ec
|
|
1718
|
+
```
|
|
1719
|
+
|
|
1720
|
+
Multiple `.ec` files can be used and are merged during compilation. Resources can reference each other across files.
|
|
1721
|
+
|
|
1722
|
+
## Compilation
|
|
1723
|
+
|
|
1724
|
+
The DSL compiles to EventCatalog's directory structure:
|
|
1725
|
+
|
|
1726
|
+
```
|
|
1727
|
+
catalog.ec --> domains/Orders/index.mdx
|
|
1728
|
+
domains/Orders/services/OrderService/index.mdx
|
|
1729
|
+
events/OrderCreated/index.mdx
|
|
1730
|
+
commands/ProcessPayment/index.mdx
|
|
1731
|
+
channels/orders-topic/index.mdx
|
|
1732
|
+
users/dboyne.mdx
|
|
1733
|
+
teams/orders-team.mdx
|
|
1734
|
+
...
|
|
1735
|
+
```
|
|
1736
|
+
|
|
1737
|
+
Each resource becomes a markdown file with YAML frontmatter matching EventCatalog's content collection schemas.
|
|
1738
|
+
|
|
1739
|
+
---
|
|
1740
|
+
|
|
1741
|
+
# Visualizer
|
|
1742
|
+
|
|
1743
|
+
The `visualizer` block separates **resource definition** from **visualization**. Resources defined outside a visualizer block exist in the catalog but are not rendered visually. Only resources placed inside a `visualizer` block (or referenced from one) appear in the visual graph.
|
|
1744
|
+
|
|
1745
|
+
This separation lets teams maintain a single source of truth for all resources while controlling exactly what gets visualized and how.
|
|
1746
|
+
|
|
1747
|
+
```
|
|
1748
|
+
visualizer <id> {
|
|
1749
|
+
name "<display name>"
|
|
1750
|
+
summary "<text>"
|
|
1751
|
+
|
|
1752
|
+
// Display options
|
|
1753
|
+
legend true|false
|
|
1754
|
+
search true|false
|
|
1755
|
+
toolbar true|false
|
|
1756
|
+
focus-mode true|false
|
|
1757
|
+
animated true|false
|
|
1758
|
+
style default|post-it
|
|
1759
|
+
|
|
1760
|
+
// Resources to visualize (inline or reference)
|
|
1761
|
+
<resource definitions or references>
|
|
1762
|
+
}
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1765
|
+
## Why Visualizer Exists
|
|
1766
|
+
|
|
1767
|
+
A `.ec` file can define dozens of resources — domains, services, events, channels, and more. Without a visualizer block, there is no way to control which resources appear in the visual graph or how they are presented.
|
|
1768
|
+
|
|
1769
|
+
The visualizer block makes visualization **explicit**:
|
|
1770
|
+
|
|
1771
|
+
- Define resources anywhere (top-level, imported files)
|
|
1772
|
+
- Choose what to visualize by placing resources inside a `visualizer` block
|
|
1773
|
+
- Configure display options per visualization
|
|
1774
|
+
|
|
1775
|
+
## Multiple Visualizer Blocks
|
|
1776
|
+
|
|
1777
|
+
A file can contain multiple `visualizer` blocks, each presenting a different view over the same resources. Tools (e.g. the playground) allow switching between them.
|
|
1778
|
+
|
|
1779
|
+
```
|
|
1780
|
+
// Shared resources
|
|
1781
|
+
event OrderCreated {
|
|
1782
|
+
version 1.0.0
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
event PaymentProcessed {
|
|
1786
|
+
version 1.0.0
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
service OrderService {
|
|
1790
|
+
version 1.0.0
|
|
1791
|
+
sends event OrderCreated
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
service PaymentService {
|
|
1795
|
+
version 1.0.0
|
|
1796
|
+
receives event OrderCreated
|
|
1797
|
+
sends event PaymentProcessed
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// View 1: Order flow only
|
|
1801
|
+
visualizer orders {
|
|
1802
|
+
name "Order Flow"
|
|
1803
|
+
|
|
1804
|
+
service OrderService
|
|
1805
|
+
event OrderCreated
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// View 2: Full payment pipeline
|
|
1809
|
+
visualizer payments {
|
|
1810
|
+
name "Payment Pipeline"
|
|
1811
|
+
|
|
1812
|
+
service OrderService
|
|
1813
|
+
service PaymentService
|
|
1814
|
+
event OrderCreated
|
|
1815
|
+
event PaymentProcessed
|
|
1816
|
+
}
|
|
1817
|
+
```
|
|
1818
|
+
|
|
1819
|
+
## Resources Without a Visualizer
|
|
1820
|
+
|
|
1821
|
+
Resources defined outside any `visualizer` block are valid. They can be:
|
|
1822
|
+
|
|
1823
|
+
- Imported by other `.ec` files
|
|
1824
|
+
- Referenced from within a `visualizer` block
|
|
1825
|
+
- Used for compilation to EventCatalog's markdown format
|
|
1826
|
+
|
|
1827
|
+
If a file contains no `visualizer` block, no visual graph is produced.
|
|
1828
|
+
|
|
1829
|
+
## Inline vs. Reference
|
|
1830
|
+
|
|
1831
|
+
Resources inside a visualizer can be defined inline (full definition) or referenced by name:
|
|
1832
|
+
|
|
1833
|
+
**Inline** — defines and visualizes the resource:
|
|
1834
|
+
|
|
1835
|
+
```
|
|
1836
|
+
visualizer main {
|
|
1837
|
+
name "My Architecture"
|
|
1838
|
+
|
|
1839
|
+
service OrderService {
|
|
1840
|
+
version 1.0.0
|
|
1841
|
+
sends event OrderCreated
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
```
|
|
1845
|
+
|
|
1846
|
+
**Reference** — visualizes a resource defined elsewhere:
|
|
1847
|
+
|
|
1848
|
+
```
|
|
1849
|
+
service OrderService {
|
|
1850
|
+
version 1.0.0
|
|
1851
|
+
sends event OrderCreated
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
visualizer main {
|
|
1855
|
+
name "My Architecture"
|
|
1856
|
+
service OrderService
|
|
1857
|
+
}
|
|
1858
|
+
```
|
|
1859
|
+
|
|
1860
|
+
When a resource is referenced, the visualizer enriches the node with metadata from the matching top-level definition.
|
|
1861
|
+
|
|
1862
|
+
## Display Options
|
|
1863
|
+
|
|
1864
|
+
| Property | Type | Default | Description |
|
|
1865
|
+
| ------------ | ------- | --------- | -------------------------------------- |
|
|
1866
|
+
| `name` | string | — | Display title for the visualization |
|
|
1867
|
+
| `summary` | string | — | Description of the visualization |
|
|
1868
|
+
| `legend` | boolean | `true` | Show the node type legend |
|
|
1869
|
+
| `search` | boolean | `true` | Show the search bar |
|
|
1870
|
+
| `toolbar` | boolean | `true` | Show the toolbar (export, zoom, etc.) |
|
|
1871
|
+
| `focus-mode` | boolean | `true` | Enable focus mode for individual nodes |
|
|
1872
|
+
| `animated` | boolean | `true` | Animate edges |
|
|
1873
|
+
| `style` | enum | `default` | Visual style: `default` or `post-it` |
|
|
1874
|
+
|
|
1875
|
+
## Example
|
|
1876
|
+
|
|
1877
|
+
```
|
|
1878
|
+
// Teams
|
|
1879
|
+
team orders-team {
|
|
1880
|
+
name "Orders Team"
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// Resources
|
|
1884
|
+
channel orders-topic {
|
|
1885
|
+
version 1.0.0
|
|
1886
|
+
protocol "Kafka"
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// Visualizer with display options
|
|
1890
|
+
visualizer order-architecture {
|
|
1891
|
+
name "Order Architecture"
|
|
1892
|
+
summary "Core order processing services"
|
|
1893
|
+
legend true
|
|
1894
|
+
search true
|
|
1895
|
+
animated false
|
|
1896
|
+
style post-it
|
|
1897
|
+
|
|
1898
|
+
domain Orders {
|
|
1899
|
+
version 1.0.0
|
|
1900
|
+
owner orders-team
|
|
1901
|
+
|
|
1902
|
+
service OrderService {
|
|
1903
|
+
version 1.0.0
|
|
1904
|
+
sends event OrderCreated to orders-topic
|
|
1905
|
+
receives command CreateOrder
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
service NotificationService {
|
|
1909
|
+
version 1.0.0
|
|
1910
|
+
receives event OrderCreated
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
```
|
|
1915
|
+
|
|
1916
|
+
## EBNF
|
|
1917
|
+
|
|
1918
|
+
```ebnf
|
|
1919
|
+
visualizer_decl = "visualizer" identifier "{" { visualizer_body } "}" ;
|
|
1920
|
+
visualizer_body = name_prop | summary_prop | annotation
|
|
1921
|
+
| legend_prop | search_prop | toolbar_prop
|
|
1922
|
+
| focus_mode_prop | animated_prop | style_prop
|
|
1923
|
+
| domain_decl | service_decl | event_decl | command_decl
|
|
1924
|
+
| query_decl | channel_decl | container_decl
|
|
1925
|
+
| data_product_decl | flow_decl
|
|
1926
|
+
| actor_decl | external_system_decl
|
|
1927
|
+
| service_ref_stmt | domain_ref_stmt
|
|
1928
|
+
| event_ref_stmt | command_ref_stmt | query_ref_stmt
|
|
1929
|
+
| channel_ref_stmt | data_product_ref_stmt | flow_ref_stmt
|
|
1930
|
+
| container_ref_stmt ;
|
|
1931
|
+
legend_prop = "legend" bool_lit ;
|
|
1932
|
+
search_prop = "search" bool_lit ;
|
|
1933
|
+
toolbar_prop = "toolbar" bool_lit ;
|
|
1934
|
+
focus_mode_prop = "focus-mode" bool_lit ;
|
|
1935
|
+
animated_prop = "animated" bool_lit ;
|
|
1936
|
+
style_prop = "style" ( "default" | "post-it" ) ;
|
|
1937
|
+
```
|
|
1938
|
+
|
|
1939
|
+
---
|