使用PlantUML绘制C4模型风格的软件架构图

1 C4模型回顾

最简概括4个C:

  • Context Diagram:背景图,系统如何融入环境(包括用户和其他系统)
  • Container Diagram:容器图,系统如何由容器(不是容器技术,应用程序 or 存储)组成
  • Compoment Diagram:组件图,展示特定容器的实现
  • Code Diagram:代码图,使用UML展示

上述4个C从上到下是逐步放大的,一般常画的是前2个。

更详细的,建议反复阅读官方原文

2 安装工具

  • VSCode
  • 插件:plantUML
  • 库:Graphviz

3 配置C4-PlantUML

使用国内镜像

git clone https://github.com/plantuml-stdlib/C4-PlantUML.git

下载后,需要做一些修改,针对根目录下*.puml:

将远程引用修改问本地引用:

' convert it with additional command line argument -DRELATIVE_INCLUDE="." to use locally
'!if %variable_exists("RELATIVE_INCLUDE")
'  !include %get_variable_value("RELATIVE_INCLUDE")/C4.puml
'!else
'  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml
'!endif
!include ./C4.puml

诚然,上述修改过程非常蛋疼,我会在本文末尾给出另一种解决方案

4 绘制Context图

Context图反应了新建设系统与用户、其他系统的交互关系,代码如下:

@startuml
!include /Users/coder4/code/C4-PlantUML/C4_Context.puml

Person(user, "用户", "小程序电商系统的潜在用户")

AddElementTag("existing_system", $bgColor="#777", $borderColor="#555", $fontColor="white")

System_Boundary(c1, "小程序电商系统") {
    System(weappMallSystem, "小程序商城", "提供 浏览商品,添加购物车,下单,支付,联系客服,查询物流 等功能")
    System(ecomMiddlePlatform, "电商中台", "提供 商品中台 库存中台 物流中台 订单中台 等中台能力", $tags="existing_system")
}

System_Ext(weixinPlatform, "微信公众平台", "提供 用户授权、支付、推送 等功能")

Rel_D(user, weappMallSystem, "使用")
Rel_U(weixinPlatform, user, "交互")
Rel(weappMallSystem, ecomMiddlePlatform, "交互")
Rel(ecomMiddlePlatform, weixinPlatform, "交互")
Rel(weappMallSystem, weixinPlatform, "交互")

SHOW_FLOATING_LEGEND()

@enduml

解释一下:

  • 我们用绝对路径引入了C4-PlantUML,是的,这非常蛋疼,请看文末结尾给的替代方案
  • Person是用户,System是系统,System_Ext是外部系统
  • Rel是关系,可以附加方向,例如Rel_D希望关系在下部,可选值UDLR
  • System_Boundary是系统边界
  • AddElementTag自定义了查询边界,这主要是解决没有已存在系统这个组件的问题
  • SHOW_FLOATING_LEGEND在合适位置展示图例,如果不加这个,图例会展示在每一个元素上

整体渲染图如下:

 

 

 

 

 

 

 

5 绘制Container图

Container图是对Context图中,欲建设系统的放大,展示了其中每一个Container(客户端 or 服务 or 存储 or 消息队列)

代码如下:

@startuml
!include /Users/coder4/code/C4-PlantUML/C4_Container.puml
!define DEVICONS /Users/coder4/code/plantuml-icon-font-sprites/devicons
!include DEVICONS/java.puml
!include DEVICONS/mysql.puml
!include DEVICONS/webplatform.puml

!define FA5 /Users/coder4/code/plantuml-icon-font-sprites/font-awesome-5
!include FA5/weixin.puml

Person(user, "用户", "小程序电商系统的潜在用户")

System_Boundary(c1, "小程序电商系统") {
    Container(miniApp, "微信小程序", "mini", "用户交互的小程序前端", $sprite="weixin")
    Container(mallService, "商城微服务", "Java, Spring Cloud, Tomcat", "商品浏览、详情的业务逻辑", $sprite="java")
    Container(orderService, "订单微服务", "Java, Spring Cloud, Tomcat", "下单、订单的业务逻辑", $sprite="java")
    Container(miniService, "小程序微服务", "Java, Spring Cloud, Tomcat", "微信授权、推送等业务逻辑", $sprite="java")
    ContainerDb(database, "Database", "MySQL", "RDBMS数据库", $sprite="mysql")
}

System_Ext(ecomMiddlePlatform, "电商中台", "提供 商品中台 库存中台 物流中台 订单中台 等中台能力")

System_Ext(weixinPlatform, "微信公众平台", "提供 用户授权、支付、推送 等功能")

Rel_R(user, miniApp, "使用")
Rel(miniApp, mallService, "调用", "https")
Rel(miniApp, orderService, "调用", "https")
Rel(miniApp, miniService, "调用", "https")
Rel(orderService, miniService, "调用", "rpc")
Rel(mallService, miniService, "调用", "rpc")

Rel_L(miniService, weixinPlatform, "交互", "https")
Rel_U(weixinPlatform, user, "交互")
Rel_R(orderService, ecomMiddlePlatform, "调用", "rpc")
Rel_R(mallService, ecomMiddlePlatform, "调用", "rpc")

Rel(mallService, database, "读/写")
Rel(orderService, database, "读/写")
Rel(miniService, database, "读/写")

SHOW_FLOATING_LEGEND()


@enduml

解释一下:

  • 使用了绝对定位,不废话了,看文末解决方案
  • 引入了FontAwaresome for PlantUML,这里主要是java、mysql和weixin
  • Container每一个容器,这里要写名称、技术栈、用途

最终图如下:

 

 

 

 

 

 

6 绘制Component图

Component图是对Container图的放大,可以认为是由一个或多个类组成的逻辑组

代码如下:

@startuml
!include /Users/coder4/code/C4-PlantUML/C4_Component.puml

LAYOUT_WITH_LEGEND()

title Component diagram for Internet Banking System - API Application

Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
Container(ma, "Mobile App", "Xamarin", "Provides a limited subset ot the internet banking functionality to customers via their mobile mobile device.")
ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Container_Boundary(api, "API Application") {
    Component(sign, "Sign In Controller", "MVC Rest Controlle", "Allows users to sign in to the internet banking system")
    Component(accounts, "Accounts Summary Controller", "MVC Rest Controller", "Provides customers with a summary of their bank accounts")
    Component(security, "Security Component", "Spring Bean", "Provides functionality related to singing in, changing passwords, etc.")
    Component(mbsfacade, "Mainframe Banking System Facade", "Spring Bean", "A facade onto the mainframe banking system.")

    Rel(sign, security, "Uses")
    Rel(accounts, mbsfacade, "Uses")
    Rel(security, db, "Read & write to", "JDBC")
    Rel(mbsfacade, mbs, "Uses", "XML/HTTPS")
}

Rel(spa, sign, "Uses", "JSON/HTTPS")
Rel(spa, accounts, "Uses", "JSON/HTTPS")

Rel(ma, sign, "Uses", "JSON/HTTPS")
Rel(ma, accounts, "Uses", "JSON/HTTPS")
@enduml

最终图如下:

7 国内的 C4-PlantUML / plantuml-icon-font-sprites 镜像

如果你仔细阅读github的例子,会发现原始例子都是直接引用https://githubcontent的,但是众所周知的原因,国内没发用。

实际上,我们只需在国内有一份https的拷贝,并且对原始代码(中的依赖)稍做替换,即可实现。

我在gitee建了两个镜像:

具体用法可以点进去看下,记得Star 🙂

2 thoughts on “使用PlantUML绘制C4模型风格的软件架构图

  1. Litao

    试试这个:

    @startuml
    !define RELATIVE_INCLUDE C:\myWORKSPACE\tools\C4-PlantUML
    !include RELATIVE_INCLUDE/C4_Dynamic.puml

    Reply

Leave a Reply

Your email address will not be published.