@ -0,0 +1,97 @@
|
||||
"""
|
||||
A set of nodes and edges to visualize software architecture using the C4 model.
|
||||
"""
|
||||
import html
|
||||
import textwrap
|
||||
from diagrams import Cluster, Node, Edge
|
||||
|
||||
|
||||
def _format_node_label(name, key, description):
|
||||
"""Create a graphviz label string for a C4 node"""
|
||||
title = f'<font point-size="12"><b>{html.escape(name)}</b></font><br/>'
|
||||
subtitle = f'<font point-size="9">[{html.escape(key)}]<br/></font>' if key else ""
|
||||
text = f'<br/><font point-size="10">{_format_description(description)}</font>' if description else ""
|
||||
return f"<{title}{subtitle}{text}>"
|
||||
|
||||
|
||||
def _format_description(description):
|
||||
"""
|
||||
Formats the description string so it fits into the C4 nodes.
|
||||
|
||||
It line-breaks the description so it fits onto exactly three lines. If there are more
|
||||
than three lines, all further lines are discarded and "..." inserted on the last line to
|
||||
indicate that it was shortened. This will also html-escape the description so it can
|
||||
safely be included in a HTML label.
|
||||
"""
|
||||
wrapper = textwrap.TextWrapper(width=40, max_lines=3)
|
||||
lines = [html.escape(line) for line in wrapper.wrap(description)]
|
||||
lines += [""] * (3 - len(lines)) # fill up with empty lines so it is always three
|
||||
return "<br/>".join(lines)
|
||||
|
||||
|
||||
def _format_edge_label(description):
|
||||
"""Create a graphviz label string for a C4 edge"""
|
||||
wrapper = textwrap.TextWrapper(width=24, max_lines=3)
|
||||
lines = [html.escape(line) for line in wrapper.wrap(description)]
|
||||
text = "<br/>".join(lines)
|
||||
return f'<<font point-size="10">{text}</font>>'
|
||||
|
||||
|
||||
def C4Node(name, technology="", description="", type="Container", **kwargs):
|
||||
key = f"{type}: {technology}" if technology else type
|
||||
node_attributes = {
|
||||
"label": _format_node_label(name, key, description),
|
||||
"labelloc": "c",
|
||||
"shape": "rect",
|
||||
"width": "2.6",
|
||||
"height": "1.6",
|
||||
"fixedsize": "true",
|
||||
"style": "filled",
|
||||
"fillcolor": "dodgerblue3",
|
||||
"fontcolor": "white",
|
||||
}
|
||||
# collapse boxes to a smaller form if they don't have a description
|
||||
if not description:
|
||||
node_attributes.update({"width": "2", "height": "1"})
|
||||
node_attributes.update(kwargs)
|
||||
return Node(**node_attributes)
|
||||
|
||||
|
||||
def Container(name, technology="", description="", **kwargs):
|
||||
return C4Node(name, technology=technology, description=description, type="Container")
|
||||
|
||||
|
||||
def Database(name, technology="", description="", **kwargs):
|
||||
return C4Node(name, technology=technology, description=description, type="Database", shape="cylinder", labelloc="b")
|
||||
|
||||
|
||||
def System(name, description="", external=False, **kwargs):
|
||||
type = "External System" if external else "System"
|
||||
fillcolor = "gray60" if external else "dodgerblue4"
|
||||
return C4Node(name, description=description, type=type, fillcolor=fillcolor)
|
||||
|
||||
|
||||
def Person(name, description="", external=False, **kwargs):
|
||||
type = "External Person" if external else "Person"
|
||||
fillcolor = "gray60" if external else "dodgerblue4"
|
||||
style = "rounded,filled"
|
||||
return C4Node(name, description=description, type=type, fillcolor=fillcolor, style=style)
|
||||
|
||||
|
||||
def SystemBoundary(name, **kwargs):
|
||||
graph_attributes = {
|
||||
"label": html.escape(name),
|
||||
"bgcolor": "white",
|
||||
"margin": "16",
|
||||
"style": "dashed",
|
||||
}
|
||||
graph_attributes.update(kwargs)
|
||||
return Cluster(name, graph_attr=graph_attributes)
|
||||
|
||||
|
||||
def Relationship(label="", **kwargs):
|
||||
edge_attribtues = {"style": "dashed", "color": "gray60"}
|
||||
if label:
|
||||
edge_attribtues.update({"label": _format_edge_label(label)})
|
||||
edge_attribtues.update(kwargs)
|
||||
return Edge(**edge_attribtues)
|
@ -0,0 +1,12 @@
|
||||
"""
|
||||
DigitalOcean provides a set of services for DigitalOcean provider.
|
||||
"""
|
||||
|
||||
from diagrams import Node
|
||||
|
||||
|
||||
class _DigitalOcean(Node):
|
||||
_provider = "digitalocean"
|
||||
_icon_dir = "resources/digitalocean"
|
||||
|
||||
fontcolor = "#ffffff"
|
@ -0,0 +1,43 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _DigitalOcean
|
||||
|
||||
|
||||
class _Compute(_DigitalOcean):
|
||||
_type = "compute"
|
||||
_icon_dir = "resources/digitalocean/compute"
|
||||
|
||||
|
||||
class Containers(_Compute):
|
||||
_icon = "containers.png"
|
||||
|
||||
|
||||
class Docker(_Compute):
|
||||
_icon = "docker.png"
|
||||
|
||||
|
||||
class DropletConnect(_Compute):
|
||||
_icon = "droplet-connect.png"
|
||||
|
||||
|
||||
class DropletSnapshot(_Compute):
|
||||
_icon = "droplet-snapshot.png"
|
||||
|
||||
|
||||
class Droplet(_Compute):
|
||||
_icon = "droplet.png"
|
||||
|
||||
|
||||
class K8SCluster(_Compute):
|
||||
_icon = "k8s-cluster.png"
|
||||
|
||||
|
||||
class K8SNodePool(_Compute):
|
||||
_icon = "k8s-node-pool.png"
|
||||
|
||||
|
||||
class K8SNode(_Compute):
|
||||
_icon = "k8s-node.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,27 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _DigitalOcean
|
||||
|
||||
|
||||
class _Database(_DigitalOcean):
|
||||
_type = "database"
|
||||
_icon_dir = "resources/digitalocean/database"
|
||||
|
||||
|
||||
class DbaasPrimaryStandbyMore(_Database):
|
||||
_icon = "dbaas-primary-standby-more.png"
|
||||
|
||||
|
||||
class DbaasPrimary(_Database):
|
||||
_icon = "dbaas-primary.png"
|
||||
|
||||
|
||||
class DbaasReadOnly(_Database):
|
||||
_icon = "dbaas-read-only.png"
|
||||
|
||||
|
||||
class DbaasStandby(_Database):
|
||||
_icon = "dbaas-standby.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,47 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _DigitalOcean
|
||||
|
||||
|
||||
class _Network(_DigitalOcean):
|
||||
_type = "network"
|
||||
_icon_dir = "resources/digitalocean/network"
|
||||
|
||||
|
||||
class Certificate(_Network):
|
||||
_icon = "certificate.png"
|
||||
|
||||
|
||||
class DomainRegistration(_Network):
|
||||
_icon = "domain-registration.png"
|
||||
|
||||
|
||||
class Domain(_Network):
|
||||
_icon = "domain.png"
|
||||
|
||||
|
||||
class Firewall(_Network):
|
||||
_icon = "firewall.png"
|
||||
|
||||
|
||||
class FloatingIp(_Network):
|
||||
_icon = "floating-ip.png"
|
||||
|
||||
|
||||
class InternetGateway(_Network):
|
||||
_icon = "internet-gateway.png"
|
||||
|
||||
|
||||
class LoadBalancer(_Network):
|
||||
_icon = "load-balancer.png"
|
||||
|
||||
|
||||
class ManagedVpn(_Network):
|
||||
_icon = "managed-vpn.png"
|
||||
|
||||
|
||||
class Vpc(_Network):
|
||||
_icon = "vpc.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,27 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _DigitalOcean
|
||||
|
||||
|
||||
class _Storage(_DigitalOcean):
|
||||
_type = "storage"
|
||||
_icon_dir = "resources/digitalocean/storage"
|
||||
|
||||
|
||||
class Folder(_Storage):
|
||||
_icon = "folder.png"
|
||||
|
||||
|
||||
class Space(_Storage):
|
||||
_icon = "space.png"
|
||||
|
||||
|
||||
class VolumeSnapshot(_Storage):
|
||||
_icon = "volume-snapshot.png"
|
||||
|
||||
|
||||
class Volume(_Storage):
|
||||
_icon = "volume.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,15 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _Programming
|
||||
|
||||
|
||||
class _Runtime(_Programming):
|
||||
_type = "runtime"
|
||||
_icon_dir = "resources/programming/runtime"
|
||||
|
||||
|
||||
class Dapr(_Runtime):
|
||||
_icon = "dapr.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,15 @@
|
||||
# This module is automatically generated by autogen.sh. DO NOT EDIT.
|
||||
|
||||
from . import _Saas
|
||||
|
||||
|
||||
class _Communication(_Saas):
|
||||
_type = "communication"
|
||||
_icon_dir = "resources/saas/communication"
|
||||
|
||||
|
||||
class Twilio(_Communication):
|
||||
_icon = "twilio.png"
|
||||
|
||||
|
||||
# Aliases
|
@ -0,0 +1,77 @@
|
||||
---
|
||||
id: c4
|
||||
title: C4
|
||||
---
|
||||
|
||||
## C4 Diagrams
|
||||
|
||||
[C4](https://c4model.com/) is a standardized model to visualize software architecture.
|
||||
You can generate C4 diagrams by using the node and edge classes from the `diagrams.c4` package:
|
||||
|
||||
```python
|
||||
from diagrams import Diagram
|
||||
from diagrams.c4 import Person, Container, Database, System, SystemBoundary, Relationship
|
||||
|
||||
graph_attr = {
|
||||
"splines": "spline",
|
||||
}
|
||||
|
||||
with Diagram("Container diagram for Internet Banking System", direction="TB", graph_attr=graph_attr):
|
||||
customer = Person(
|
||||
name="Personal Banking Customer", description="A customer of the bank, with personal bank accounts."
|
||||
)
|
||||
|
||||
with SystemBoundary("Internet Banking System"):
|
||||
webapp = Container(
|
||||
name="Web Application",
|
||||
technology="Java and Spring MVC",
|
||||
description="Delivers the static content and the Internet banking single page application.",
|
||||
)
|
||||
|
||||
spa = Container(
|
||||
name="Single-Page Application",
|
||||
technology="Javascript and Angular",
|
||||
description="Provides all of the Internet banking functionality to customers via their web browser.",
|
||||
)
|
||||
|
||||
mobileapp = Container(
|
||||
name="Mobile App",
|
||||
technology="Xamarin",
|
||||
description="Provides a limited subset of the Internet banking functionality to customers via their mobile device.",
|
||||
)
|
||||
|
||||
api = Container(
|
||||
name="API Application",
|
||||
technology="Java and Spring MVC",
|
||||
description="Provides Internet banking functionality via a JSON/HTTPS API.",
|
||||
)
|
||||
|
||||
database = Database(
|
||||
name="Database",
|
||||
technology="Oracle Database Schema",
|
||||
description="Stores user registration information, hashed authentication credentials, access logs, etc.",
|
||||
)
|
||||
|
||||
email = System(name="E-mail System", description="The internal Microsoft Exchange e-mail system.", external=True)
|
||||
|
||||
mainframe = System(
|
||||
name="Mainframe Banking System",
|
||||
description="Stores all of the core banking information about customers, accounts, transactions, etc.",
|
||||
external=True,
|
||||
)
|
||||
|
||||
customer >> Relationship("Visits bigbank.com/ib using [HTTPS]") >> webapp
|
||||
customer >> Relationship("Views account balances, and makes payments using") >> [spa, mobileapp]
|
||||
webapp >> Relationship("Delivers to the customer's web browser") >> spa
|
||||
spa >> Relationship("Make API calls to [JSON/HTTPS]") >> api
|
||||
mobileapp >> Relationship("Make API calls to [JSON/HTTPS]") >> api
|
||||
|
||||
api >> Relationship("reads from and writes to") >> database
|
||||
api >> Relationship("Sends email using [SMTP]") >> email
|
||||
api >> Relationship("Makes API calls to [XML/HTTPS]") >> mainframe
|
||||
customer << Relationship("Sends e-mails to") << email
|
||||
```
|
||||
|
||||
It will produce the following diagram:
|
||||
|
||||

|
@ -0,0 +1,43 @@
|
||||
---
|
||||
id: digitalocean
|
||||
title: DigitalOcean
|
||||
---
|
||||
|
||||
Node classes list of digitalocean provider.
|
||||
|
||||
## digitalocean.compute
|
||||
|
||||
- **diagrams.digitalocean.compute.Containers**
|
||||
- **diagrams.digitalocean.compute.Docker**
|
||||
- **diagrams.digitalocean.compute.DropletConnect**
|
||||
- **diagrams.digitalocean.compute.DropletSnapshot**
|
||||
- **diagrams.digitalocean.compute.Droplet**
|
||||
- **diagrams.digitalocean.compute.K8SCluster**
|
||||
- **diagrams.digitalocean.compute.K8SNodePool**
|
||||
- **diagrams.digitalocean.compute.K8SNode**
|
||||
|
||||
## digitalocean.database
|
||||
|
||||
- **diagrams.digitalocean.database.DbaasPrimaryStandbyMore**
|
||||
- **diagrams.digitalocean.database.DbaasPrimary**
|
||||
- **diagrams.digitalocean.database.DbaasReadOnly**
|
||||
- **diagrams.digitalocean.database.DbaasStandby**
|
||||
|
||||
## digitalocean.network
|
||||
|
||||
- **diagrams.digitalocean.network.Certificate**
|
||||
- **diagrams.digitalocean.network.DomainRegistration**
|
||||
- **diagrams.digitalocean.network.Domain**
|
||||
- **diagrams.digitalocean.network.Firewall**
|
||||
- **diagrams.digitalocean.network.FloatingIp**
|
||||
- **diagrams.digitalocean.network.InternetGateway**
|
||||
- **diagrams.digitalocean.network.LoadBalancer**
|
||||
- **diagrams.digitalocean.network.ManagedVpn**
|
||||
- **diagrams.digitalocean.network.Vpc**
|
||||
|
||||
## digitalocean.storage
|
||||
|
||||
- **diagrams.digitalocean.storage.Folder**
|
||||
- **diagrams.digitalocean.storage.Space**
|
||||
- **diagrams.digitalocean.storage.VolumeSnapshot**
|
||||
- **diagrams.digitalocean.storage.Volume**
|
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |