Diagrams and Components¶
Note
You can find a detailed description of the different C4 diagram types and components in the corresponding sections of the documentation.
For the boundary between backend-neutral model data and renderer-specific hints, see Portable core and backend extensions.
Diagrams¶
Diagram is a primary object representing a diagram.
from c4.diagrams.core import Diagram
with Diagram("Simple Diagram") as diagram:
# Declare diagram components here
Each C4 diagram type is implemented as a subclass of Diagram:
from c4 import (
SystemContextDiagram,
SystemLandscapeDiagram,
ContainerDiagram,
ComponentDiagram,
DynamicDiagram,
DeploymentDiagram,
)
Note
Diagram is a low-level abstraction and is not intended to be instantiated directly.
Always use one of the concrete diagram classes (SystemContextDiagram,
ContainerDiagram, ComponentDiagram, etc.), as they define the semantics,
constraints, and rendering rules specific to each C4 diagram type.
Elements¶
Element represents a C4 abstraction such as a System, Container, or Component.
Elements must be declared within a diagram context:
from c4.diagrams.core import Diagram, Element
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
assert diagram.elements == [element1, element2]
Each C4 element type is implemented as a subclass of Element:
from c4 import (
Person,
PersonExt,
System,
SystemDb,
SystemDbExt,
SystemExt,
SystemQueue,
SystemQueueExt,
Component,
ComponentDb,
ComponentDbExt,
ComponentExt,
ComponentQueue,
ComponentQueueExt,
Container,
ContainerDb,
ContainerDbExt,
ContainerExt,
ContainerQueue,
ContainerQueueExt,
DeploymentNode,
Node,
)
from c4.contrib.c4_macros import NodeLeft, NodeRight
from c4.contrib.plantuml import DeploymentNodeLeft, DeploymentNodeRight
Relationships¶
Relationship represents a connection between elements and may include additional
properties such as direction and label.
A relationship object contains four primary attributes: label, relationship_type, from_element, and to_element.
The portable core relationship shortcut is Rel:
from c4.diagrams.core import Rel
PlantUML-specific relationship
PlantUML-specific relationship shortcuts for direction, bidirectional
relationships, back relationships, and neighbor relationships are available
from c4.contrib.plantuml:
from c4.contrib.plantuml import (
BiRel,
BiRelD,
BiRelDown,
BiRelL,
BiRelLeft,
BiRelNeighbor,
BiRelR,
BiRelRight,
BiRelU,
BiRelUp,
RelBack,
RelBackNeighbor,
RelD,
RelDown,
RelL,
RelLeft,
RelNeighbor,
RelR,
RelRight,
RelU,
RelUp,
)
Relationships must be declared within a diagram context:
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element1 >> Rel("Uses") >> element2
assert diagram.relationships[0].label == "Uses"
assert diagram.relationships[0].from_element == element1
assert diagram.relationships[0].to_element == element2
There are several ways to declare relationships:
Simple cases¶
A relationship can be declared using its label:
from c4.diagrams.core import Diagram, Element
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element1 >> "Uses" >> element2
Alternatively, a relationship can be declared using an explicit relationship object:
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element1 >> Rel("Uses") >> element2
Rel attributes
Rel lets you specify portable relationship attributes
such as technology and description.
PlantUML-specific attributes such as links, tags, and dynamic indexes belong
under the plantuml={...} kwarg. PlantUML relationship shortcuts such as
RelUp and RelLeft
are available from c4.contrib.plantuml.
In most cases, a plain string label ("Uses") is enough.
Using the pipe syntax:
from c4.diagrams.core import Diagram, Element
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element1 >> element2 | "Uses"
Or by calling the method:
from c4.diagrams.core import Diagram, Element
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element1.uses(element2, label="Uses")
All syntaxes above produce the same Relationship in the diagram.
Reversed direction¶
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element2 << Rel("Uses") << element1
Or by calling the method:
from c4.diagrams.core import Diagram, Element
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element2.used_by(element1, label="Uses")
One-to-many relationships¶
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element3 = Element(label="Element 3")
element1 >> Rel("Uses") >> [element2, element3]
assert diagram.relationships[0].label == "Uses"
assert diagram.relationships[0].from_element == element1
assert diagram.relationships[0].to_element == element2
assert diagram.relationships[1].label == "Uses"
assert diagram.relationships[1].from_element == element1
assert diagram.relationships[1].to_element == element3
or reversed:
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element3 = Element(label="Element 3")
[element2, element3] << Rel("Uses") << element1
Many-to-one relationships¶
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element3 = Element(label="Element 3")
[element2, element3] >> Rel("Uses") >> element1
or reversed:
from c4.diagrams.core import Diagram, Element, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
element2 = Element(label="Element 2")
element3 = Element(label="Element 3")
element1 << Rel("Uses") << [element2, element3]
Boundaries¶
Boundary allows grouping (or clustering) elements into an isolated logical container.
Elements that belong to a boundary must be created within a boundary context:
from c4.diagrams.core import Diagram, Element, Boundary, Rel
with Diagram("Simple Diagram") as diagram:
element1 = Element(label="Element 1")
with Boundary(label="Boundary") as boundary:
element2 = Element(label="Element 2")
element3 = Element(label="Element 3")
element1 >> Rel("Uses") >> [element2, element3]
assert diagram.elements == [element1]
assert boundary.elements == [element2, element3]
There are several types of boundaries:
from c4 import (
SystemBoundary,
ContainerBoundary,
EnterpriseBoundary,
Node,
DeploymentNode,
)
from c4.contrib.c4_macros import NodeLeft, NodeRight
from c4.contrib.plantuml import DeploymentNodeLeft, DeploymentNodeRight
Examples¶
You can find all the examples on the examples page.
System context diagram¶
from c4 import SystemContextDiagram, Person, System, SystemExt
with SystemContextDiagram("Internet Banking System") as diagram:
client = Person(
label="Personal Banking Customer",
description="A customer of the bank with one or more personal bank accounts"
)
banking_system = System(
label="Internal Banking System",
description=(
"Allow customers to view information about their bank accounts "
"and make payments via the web."
)
)
aws_email = SystemExt(
label="Amazon Web Services Simple Email Service",
description="Cloud-based email service provider."
)
core_banking_system = System(
label="Core Banking System",
description=(
"Handles core banking functions including customer information, "
"bank account management, transactions, etc."
)
)
client >> "Views account balances and makes payments using" >> banking_system
banking_system >> "Sends e-mails to customers using" >> aws_email
aws_email >> "Sends e-mails to" >> client
banking_system >> "Gets bank account information from and makes payments using" >> core_banking_system
The diagram above can be rendered into the following PlantUML diagram:
Container diagram¶
from c4 import (
ContainerDiagram,
Person,
System,
SystemExt,
ContainerBoundary,
Container,
ContainerDb,
Rel,
)
from c4.contrib.plantuml import RelDown, RelLeft, RelRight, RelUp
with ContainerDiagram("Internet Banking System") as diagram:
client = Person(
label="Personal Banking Customer",
description="A customer of the bank with one or more personal bank accounts"
)
aws_email = SystemExt(
label="Amazon Web Services Simple Email Service",
description="Cloud-based email service provider."
)
core_banking_system = System(
label="Core Banking System",
description=(
"Handles core banking functions including customer information, "
"bank account management, transactions, etc."
)
)
with ContainerBoundary(label="Internal Banking System"):
static = Container(
label="Static Content",
technology="Directory",
description="HTML, CSS, JavaScript, etc.",
)
ui = Container(
label="UI",
technology="JavaScript and Angular",
description=(
"Single-page app that provides Internet banking functionality "
"to customers via their web browser."
),
)
backend = Container(
label="Backend",
technology="Java and Spring Boot",
description=(
"Provides Internet banking functionality via a JSON/HTTP API."
)
)
statement_store = Container(
label="Statement Store",
technology="Amazon Web Services S3 Bucket",
description="Bank account statements rendered as PDF files."
)
db = ContainerDb(
label="Database",
technology="MySQL",
description="User account information, access logs, etc.",
)
client >> RelDown("Loads the UI form" ) >> static
client >> RelDown("Views account balances and makes payments using" ) >> ui
static >> RelRight("Delivers") >> ui
backend >> RelLeft("Sends e-mails to customers using", technology="AWS SES API/HTTP") >> aws_email
aws_email >> RelUp("Sends e-mails to") >> client
ui >> Rel("Makes API requests to", technology="JSON/HTTP") >> backend
backend >> RelRight("Makes API requests to", technology="XML/HTTPS") >> core_banking_system
backend >> Rel("Reads from and writes to", technology="MySQL protocol") >> db
backend >> Rel("Reads from and writes to", technology="AWS S3 API/HTTP") >> statement_store
The diagram above can be rendered into the following PlantUML diagram: