References
LikeC4 uses the lexical scope, with hoisting, almost like in JavaScript.
Scope
To understand references, we need to understand scopes first.
Example:
model { service service1 { component api component frontend }}
Every element is unique in the model, so we can add a relationship referencing them, like:
model { service service1 { component api component frontend } frontend -> api}
But if we add service2
with another api
:
model { service service1 { component api component frontend } service service2 { component api }
frontend -> api // ⛔️ Error: 'api' not found}
The reference is ambiguous, as there are two api
components in the model.
Every element creates a new scope inside {...}
, so api
is unique inside service1
and service2
,
but not in the scope of model
.
We can resolve by moving the relationship to the scope of service2
:
model { service service1 { component api component frontend } service service2 { component api
frontend -> api // ✅ This is OK, // 'api' is unique in 'service2' // 'frontend' is unique in 'model' }}
Hoisting
In LikeC4, the element, besides being hoisted in its scope, also “bubbles” to the upper scopes, if it stays unique.
We may reference something that is not yet defined but will be hoisted later.
The relationship on line 8 references graphql
defined below on line 15:
1model {2
3 service service1 {4 component api5 component frontend6
7 frontend -> api // ✅ This is OK, references to 'api' from 'service1'8 frontend -> graphql // ✅ This is OK, references to unique 'graphql'9 }10
11 frontend -> api // ⛔️ Error: 'api' is ambiguous12
13 service service2 {14 component api15 component graphql16
17 frontend -> api // ✅ This is OK, references to 'api' from 'service2'18 }19
20}
Fully qualified names
Top-level elements (placed directly in the model
block) are available globally (explained later in Workspace).
To reference nested elements we use their fully qualified names (FQN).
Example:
model { service service1 { component api component frontend } service service2 { component api }
frontend -> api // ⛔️ Error: 'api' not found frontend -> service1.api // ✅ This is OK frontend -> service2.api // ✅ This is OK}
Or even:
model { service service1 { component api component frontend { -> service2.api } } service service2 { component api }}
Some parts may be omitted, if FQN stays unique:
model { service service { component backend1 { component api } component backend2 { component api component graphql } }
frontend -> service.backend1.api // ✅ Non-ambiguous fully qualified name
frontend -> backend1.api // ✅ This is OK, 'api' is unique in 'backend1', // and 'backend1' is unique in the model // We may omit 'service'
frontend -> backend2.api // ✅ This is also OK
frontend -> service.api // ⛔️ Error: 'api' is ambiguous in 'service'
frontend -> service.graphql // ✅ This is also OK, we omit 'backend2' // as 'graphql' is unique in 'service'}