Skip to content

References

LikeC4 uses the lexical scope, with hoisting, almost like in JavaScript.

Scope

To understand references, we need to understand scopes first.
Example:

references.c4
model {
service service1 {
component api
component frontend
}
}

Every element is unique in the model, so we can add a relationship referencing them, like:

references.c4
model {
service service1 {
component api
component frontend
}
frontend -> api
}

But if we add service2 with another api:

references.c4
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:

references.c4
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:

references.c4
1
model {
2
3
service service1 {
4
component api
5
component frontend
6
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 ambiguous
12
13
service service2 {
14
component api
15
component graphql
16
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:

references.c4
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:

references.c4
model {
service service1 {
component api
component frontend {
-> service2.api
}
}
service service2 {
component api
}
}

Some parts may be omitted, if FQN stays unique:

references.c4
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'
}