Models and C4 views
An architecture model declares people, software systems, containers, datastores and components once, with their relations. C4 views draw the model at one level, each with its own composition: moving a box changes one view, renaming an entity changes all of them.
language "0.2"
model commerce { person customer "Cliente"
system shop "Commerce" { container web "Web" { technology "React" } container api "API" { technology "HTTP" } datastore db "Pedidos" { technology "PostgreSQL" } }
customer -> shop.web: "Compra" as visit shop.web -> shop.api: "HTTPS" as request shop.api -> shop.db: "SQL" as persist}
diagram containers: c4 using commerce { level container scope shop include customer, shop.web, shop.api, shop.db direction LR layout assisted theme "editorial.light"}Views compile into the same model as flow diagrams (decision 0020): entities become elements whose
ids are their paths, relations come from the model, and placement, style, class, rule,
animate, themes, presets, canvases and every layout mode work as in flow diagrams.
Models
Section titled “Models”| Declaration | Entity | Where |
|---|---|---|
person <id> ["Label"] [{ … }] |
a person who uses the systems | at the model’s root |
system <id> ["Label"] [{ … }] |
a software system | at the model’s root |
container <id> ["Label"] [{ … }] |
an application or service of a system | in a system |
datastore <id> ["Label"] [{ … }] |
a container that stores data (cylinder) | in a system |
component <id> ["Label"] [{ … }] |
a part of a container | in a container |
Entity blocks take description "…", technology "…", external (persons and systems outside
the organisation), url "…" (a link in exports, export.md) and the entities nested in
them. Any other placement is model.invalid-nesting.
- Paths. An entity is named by the ids from the root:
shop.api.orders. Siblings have distinct ids. - Relations.
a -> b: "Label" as id { technology "…"; description "…" }, at the root or in an entity block. Endpoints are looked up from the enclosing entity outwards, then from the root, after the whole model is read, so declaration order never matters: insidesystem shop { },apinamesshop.api. A relation from an entity to itself or to its own ancestor ismodel.invalid-relation; an unknown endpoint isref.unknown-entity, with close paths as hints. - Ids. Model ids share the file namespace with diagrams and themes; relation ids are unique per model.
diagram <id>: c4 using <model> { … } names a model of the file, a member import
(import commerce from "../models/commerce.meltemi") or a namespace one (using models.commerce).
| Level | scope |
Draws | Without include |
|---|---|---|---|
context |
optional, a system | persons and systems | all of them; with a scope, the scope and its neighbours |
container |
a system | the scope’s containers and datastores, persons, other systems | the scope’s children and the entities they relate to |
component |
a container | the scope’s components, the other containers of its system, persons, other systems | the scope’s children and the entities they relate to |
include a, b, …lists the entities to draw;exclude a, …removes entities from the list or from the default set.- The scope of a container or component view is drawn as a dashed boundary around the children it holds.
label shop.api "Orders API"gives an entity, or a relation by its id, another text in this view only. Inplacement,label <relation> at 50%still moves a relation’s label.
| Code | When |
|---|---|
c4.invalid-level |
level is missing or not context, container or component |
c4.scope-required |
a container or component view without scope |
c4.invalid-scope |
a scope of the wrong kind; datastores have no components |
c4.out-of-level |
an entity the level does not draw, or the scope itself, in include |
c4.unsupported-relation |
a combination that no model relation stands behind |
ref.unknown-model |
using names no model |
ref.unknown-entity |
an unknown path in scope, include or exclude |
Relations in views
Section titled “Relations in views”- Lifting. Each end of a model relation is drawn at its nearest drawn ancestor: at the context
level,
customer -> shop.webgoes fromcustomertoshop. A relation whose ends meet, or that has an end with nothing drawn above it, is not drawn. - Traceability. Every drawn relation keeps the keys of the model relations it stands for
(
sourcesin the JSON, “Stands for” in the inspector) and says when an end was lifted (derived). Direct and lifted relations keep their model id, so styles and routes name them. - No merging by default. Two model relations lifted to the same pair stay two arrows.
- Explicit combination. A relation statement in a view combines every model relation whose
drawn ends are its pair:
customer -> shop: "Usa" as usesdraws one arrow standing for all of them. A combination that no model relation stands behind isc4.unsupported-relation: views never draw arrows for convenience. Views take->and-->only;<->,<-->and the lines--and---areprop.invalid-valueon the token, naming the two.
Drawing
Section titled “Drawing”- Shapes. A person is a head above a rounded body; systems, containers and components are boxes
with their label, a type line such as
[Container: React]and their description; a datastore is a cylinder; external persons and systems are grey and dashed. - Colours. The built-in classes
c4_person,c4_system,c4_container,c4_component,c4_externalandc4_boundarycolour the kinds from the theme’s tokens. They apply first, so theme classes and rules, the view’srule,classandstylewin. - Relations. The label, with the technology on a second line:
[JSON/HTTPS].
Notes in views
Section titled “Notes in views”A view accepts one declaration of its own, note <id> "Text" [on <entity path>]: an annotation that
belongs to the view, never to the model, and is not an entity (include, exclude, label and the
relations cannot name it). It is drawn as a flow note, takes class and url in its block, and
the view’s style, class, rule and animate statements apply to it. Its place is written in the
view’s placement like any element’s.
diagram containers: c4 using commerce { level container scope shop include customer, shop.web, shop.api, shop.db note cache "Cached 5 min" on shop.api}The target of on is an entity the view draws: a path the model has but the view does not draw is
ref.unknown-element, with a hint on how to include it, and a dotted leader joins the note to it.
Excluding or removing the entity in the editor strips the clause and keeps the note.
Links to the model
Section titled “Links to the model”link <model>.<path> in a sequence or free diagram, also through imports (link commerce.shop.api,
link models.commerce.shop.api), ties an object to an entity. An object that writes no label shows
the entity’s; hover and the inspector show its kind, technology and description. A link copies
nothing else: a structural relation never becomes a message.
Imported models and renames
Section titled “Imported models and renames”- A view of an imported model compiles in the project against that model, so
validate,export,layoutand the editor see the same view. - Renaming an entity rewrites its declaration, the model’s relations, every view’s
scope,include,exclude,label, combinations, placement and styles, and every link. In VS Code, Rename Symbol reaches every file of the project; the canvas andplanEdit(renameEntity) rename within a file.