Skip to content

Extending model

You extend the model by creating new files and folders.
When LikeC4 source files are parsed, they are “merged” into a single architecture model.

You are free to organize the workspace as you want.

Assume we have the following workspace:

  • Directorycloud
    • service1.c4
    • service2.c4
  • Directoryexternals
    • amazon.c4
  • landscape.c4
  • specs.c4

This file defines the specification:

specification {
element actor {
style {
shape person
}
}
element system
element service
}

extend is a way to enrich the model and define nested elements in a separate file.

We don’t want to mess up the landscape.c4 file with the internals of the cloud.
In a separate file we extend cloud and define cloud.service1:

cloud/service1.c4
model {
// cloud is defined in landscape.c4
extend cloud {
// extend and define cloud.service1
service1 = service 'Service 1'
}
}

The element extension inherits the scope of the target (or better say parent).
For example:

cloud/service2.c4
model {
// cloud is defined in landscape.c4
extend cloud {
// extend and define cloud.service2
service2 = service 'Some Service 2'
service2 -> service1 // ✅ service1 is known inside 'cloud'
}
}

You can extend element with additional tags, links and metadata:

model {
extend cloud {
// Add tags
#additional-tag, #another-tag
// Add metadata
metadata {
prop1 'value1'
}
// Add links
link ../src/index.ts#L1-L10
}
}

When extending elements with metadata, duplicate keys from both the original element and the extension are merged into arrays:

model {
component api {
metadata {
version '1.0.0'
tags 'backend'
regions 'us-east-1'
}
}
}
// In another file
model {
extend api {
metadata {
tags 'microservice' // Merges with existing 'backend'
regions ['eu-west-1'] // Merges with existing 'us-east-1'
owner 'platform-team' // New key
}
}
}
// Result:
{
version: '1.0.0',
tags: ['backend', 'microservice'], // Merged and kept as array
regions: ['us-east-1', 'eu-west-1'], // Merged and kept as array
owner: 'platform-team'
}

Merging behavior:

  • Duplicate values are automatically de-duplicated
  • If after de-duplication there’s only one unique value, it’s stored as a string (not an array)
  • Arrays from both sides are merged and de-duplicated
model {
component api {
metadata {
environment 'production'
tags ['backend', 'api']
}
}
}
model {
extend api {
metadata {
environment 'production' // Duplicate value
tags ['api', 'critical'] // 'api' is duplicate
}
}
}
// Result:
{
environment: 'production', // Single value (de-duplicated)
tags: ['backend', 'api', 'critical'] // Merged and de-duplicated
}

You can extend the same element multiple times across different files, and all metadata will be properly merged:

file1.c4
model {
component api {
metadata {
version '2.0.0'
tags 'backend'
}
}
}
// file2.c4
model {
extend api {
metadata {
tags 'rest'
owner 'team-a'
}
}
}
// file3.c4
model {
extend api {
metadata {
tags 'microservice'
regions ['us', 'eu']
}
}
}
// Result:
{
version: '2.0.0',
tags: ['backend', 'rest', 'microservice'],
owner: 'team-a',
regions: ['us', 'eu']
}

Similar to extending elements, you can extend relations to add metadata, tags, and links from separate files.

Relations are uniquely identified by:

  • Source element
  • Target element
  • Relationship kind (if specified)
  • Title (if specified)
base.c4
specification {
element component
relationship sync
}
model {
component frontend
component api
// Untyped relation with title
frontend -> api "Makes requests"
// Typed relation with same title (different relation!)
frontend -[sync]-> api "Makes requests"
}
// extend-ops.c4
model {
// Extend the untyped relation
extend frontend -> api "Makes requests" {
metadata {
latency_p95 '150ms'
rate_limit '1000req/s'
}
}
// Extend the sync relation (different from above!)
extend frontend -[sync]-> api "Makes requests" {
metadata {
latency_p95 '80ms'
cache_enabled 'true'
}
}
}

Relation extends follow the same metadata merging rules as element extends:

  • New keys are added
  • Duplicate keys with different values become arrays
  • Duplicate keys with the same value are de-duplicated
base.c4
model {
api -> database "Queries data" {
metadata {
protocol 'TCP'
}
}
}
// extend-1.c4
model {
extend api -> database "Queries data" {
metadata {
protocol 'SSL' // Creates array
timeout '5s' // New key
}
}
}
// extend-2.c4
model {
extend api -> database "Queries data" {
metadata {
protocol 'TLS' // Adds to array
retry_policy 'exponential' // New key
}
}
}
// Result:
{
protocol: ['TCP', 'SSL', 'TLS'],
timeout: '5s',
retry_policy: 'exponential'
}