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']}