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.
Example
Section titled “Example”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}This file defines the top-level elements and landscape view:
model { customer = actor 'Customer' cloud = system 'Cloud System'}views { view index of cloud { title "Cloud System - Landscape" include * }}We keep definitions of external systems separately, inside the externals/ folder:
model { amazon = system 'Amazon Web Services' { rds = service 'Database' }}Extend element
Section titled “Extend element”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:
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:
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' }}Additional properties
Section titled “Additional properties”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 }}Metadata merging
Section titled “Metadata merging”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 filemodel { 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:
model { component api { metadata { version '2.0.0' tags 'backend' } }}
// file2.c4model { extend api { metadata { tags 'rest' owner 'team-a' } }}
// file3.c4model { extend api { metadata { tags 'microservice' regions ['us', 'eu'] } }}
// Result:{ version: '2.0.0', tags: ['backend', 'rest', 'microservice'], owner: 'team-a', regions: ['us', 'eu']}Extend relation
Section titled “Extend relation”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)
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.c4model { // 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' } }}Metadata merging
Section titled “Metadata merging”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
model { api -> database "Queries data" { metadata { protocol 'TCP' } }}
// extend-1.c4model { extend api -> database "Queries data" { metadata { protocol 'SSL' // Creates array timeout '5s' // New key } }}
// extend-2.c4model { 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'}