- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
作者:京东物流 覃玉杰 。
本文将给大家介绍一种简洁明了软件架构可视化模型——C4模型,并手把手教大家如何使用 代码 绘制出精美的C4架构图.
阅读本文之后,读者画的架构图将会是这样的:
注:该图例仅作绘图示例使用,不确保其完整性、可行性.
C4是软件架构可视化的一种方案。架构可视化,指的是用图例的方式,把软件架构设计准确、清晰、美观地表示出来。架构可视化不是指导开发者如何进行架构设计,而是指导开发者将架构设计表达出来,产出简洁直观的架构图.
架构可视化的方法有很多,主流的有“4+1”视图模型、C4模型。视图模型描述的是架构本身,架构确定之后,不管用什么模型去表达,本质上都应该是一样的,并没有优劣之分.
C4 模型是一种易于学习、对开发人员友好的软件架构图示方法,C4模型没有规定使用特定的图形、特定的建模语言来画图,因而使用者可以非常灵活地产出架构图.
C4模型将系统从上往下分为System Context, Containers, Components, Code四层视图,每一层都是对上一层的完善和展开,层层递进地对系统进行描述,如下图.
System Context(系统上下文)视图位于顶层,是软件系统架构图的起点,表达的是系统的全貌。System Context视图重点展示的是系统边界、系统相关的用户、其他支撑系统以及与本系统的交互。本层不涉及到具体细节(例如技术选型、协议、部署方案和其他低级细节),因此System Context可以很好地向非技术人员介绍系统.
作用 :清晰地展示待构建的系统、用户以及现有的IT基础设施.
范围 :待描述的核心系统以及其相关用户、支撑系统,不应该出现与核心系统无关的其他系统。例如我们要描述一个打车系统,不应该把无关联的药店系统绘制进去,并且要确保一个System Context只有一个待描述的软件系统.
主要元素 :Context内待描述的软件系统.
支持元素 :在范围内直接与主要元素中的软件系统有关联的人员(例如用户、参与者、角色或角色)和外部依赖系统。通常,这些外部依赖系统位于我们自己的软件系统边界之外.
目标受众 :软件开发团队内外的所有人,包括技术人员和非技术人员.
推荐给大多数团队 :是的.
示例:
这是该网上银行系统的系统上下文图。它显示了使用它的人,以及与该系统有关系的其他软件系统。网上银行系统是将要建设的系统,银行的个人客户使用网上银行系统查看其银行账户的信息并进行支付。网上银行系统本身使用银行现有的大型机银行系统来执行此操作,并使用银行现有的电子邮件系统向客户发送电子邮件.
图例:
Container(容器)视图是对System Context的放大,是对System Context细节的补充.
注意这里的容器,指的不是Docker等容器中间件。Container的描述范围是一个可单独运行/可部署的单元。Container一般指的是应用以及依赖的中间件,例如服务器端 Web 应用程序、单页应用程序、桌面应用程序、移动应用程序、数据库架构、文件系统、Redis、ElasticSeach、MQ等.
Container显示了软件架构的高级形状以及系统内各容器之间的职责分工.
在Container这一层,还显示了系统的主要的技术选型以及容器间的通信和交互.
作用 :展示系统整体的开发边界,体现高层次的技术选型,暴露系统内容器之间的分工交互.
范围 :单个软件系统,关注的系统内部的应用构成.
主要元素 :软件系统范围内的容器,例如Spring Boot打包后的应用,MySQL数据库、Redis、MQ等.
支持元素 :直接使用容器的人员和外部依赖系统.
目标受众 :软件开发团队内外的技术人员,包括软件架构师、开发人员和运营/支持人员.
推荐给大多数团队 :是的.
注意 :Container视图没有说明部署方案、集群、复制、故障转移等。部署相关的视图,会通过Deployment视图进行展示.
示例:
网上银行系统(此时System Contenxt中的系统已经被展开,所以用虚线框表示)由五个容器组成:服务器端 Web 应用程序、单页应用程序、移动应用程序、服务器端 API 应用程序和数据库.
该容器图的图例如下,主要是引入了数据库、APP、浏览器的图例.
将单个容器放大,则显示了该容器内部的组件。Component(组件)视图显示了一个容器是如何由许多“组件”组成的,每个组件是什么,它们的职责以及技术实现细节.
作用 :展示了可执行的容器内部构成与分工,可直接指导开发.
范围 :单个容器.
主要元素 :范围内容器内的组件,通常可以是Dubbo接口、REST接口、Service、Dao等.
支持元素 :直接连接到容器的人员和外部依赖系统.
目标受众 :软件架构师和开发人员.
推荐给大多数团队 :Component用于指导开发,当有需要时创建.
示例:
图例:
放大组件视图,则得到出组件的Code视图(代码视图).
Code视图一般采用 UML 类图、ER图等。Code视图是一个可选的详细级别,通常可以通过 IDE 等工具按需生成。除了最重要或最复杂的组件外,不建议将这种详细程度用于其他任何内容.
在注重敏捷开发的今天,一般不建议产出Code视图.
范围 :单个组件.
主要元素 :范围内组件内的代码元素(例如类、接口、对象、函数、数据库表等).
目标受众 :软件架构师和开发人员.
推荐给大多数团队 :不,大多数 IDE 可以按需生成这种级别的详细信息.
C4 模型提供了单个软件系统的静态视图,不管是 System Context、Container、Component都是针对单个软件系统的进行描述的,但在实际中软件系统不会孤立存在。为描述所有这些软件系统如何在给定的企业、组织、部门等中与其他系统组合在一起,C4采用扩展视图System Landscape (系统景观图).
系统景观图实际上只是一个没有特定关注的软件系统的系统上下文图(System Context diagram),系统景观图内的软件系统都可以采用C4进行深入分析.
适用范围 :企业/组织/部门/等.
主要元素 :与所选范围相关的人员和软件系统.
目标受众 :软件开发团队内外的技术人员和非技术人员.
示例:
图例:
Dynamic diagram(动态图)用于展示静态模型中的元素如何在运行时协作。动态图允许图表元素自由排列,并通过带有编号的箭头以指示执行顺序.
范围 :特定功能、故事、用例等.
主要元素和支持元素 :按照实际需要,可以是软件系统、容器或组件.
目标受众 :软件开发团队内外的技术人员和非技术人员.
示例:
图例:
Deployment diagram(部署图)用于说明静态模型中的软件系统(或容器)的实例在给定环境(例如生产、测试、预发、开发等)中的部署方案.
C4的部署图基于UML 部署图,但为了突出显示容器和部署节点之间的映射会做略微的简化.
部署节点表示表示软件系统/容器实例运行的位置,类似于物理基础架构(例如物理服务器或设备)、虚拟化基础架构(例如 IaaS、PaaS、虚拟机)、容器化基础架构(例如 Docker 容器)、执行环境(例如数据库服务器、Java EE web/应用服务器、Microsoft IIS)等。部署节点可以嵌套,也可以将基础设施节点包括进去,例如 DNS 服务、负载平衡器、防火墙等.
可以在部署图中随意使用 Amazon Web Services、Azure 等提供的图标,只需确保被使用的任何图标都包含在图例中,不产生歧义.
范围 :单个部署环境中的一个或多个软件系统(例如生产、暂存、开发等).
主要元素 :部署节点、软件系统实例和容器实例。 支持元素 :用于部署软件系统的基础设施节点.
目标受众 :软件开发团队内外的技术人员;包括软件架构师、开发人员、基础架构架构师和运营/支持人员.
示例:
网上银行系统的开发环境部署图:
图例 。
网上银行的生产环境部署图:
图例 。
为了确保C4模型的架构图的可读性,C4模型提供了作图规范,并且提供了CheckList供自查.
每个图都应该有一个描述图类型和范围的标题(例如“我的软件系统的系统环境图”)。 每个图表都应该有一个关键/图例来解释所使用的符号(例如形状、颜色、边框样式、线型、箭头等)。 首字母缩略词和缩写词(业务/领域或技术)应为所有受众所理解,或在图表键/图例中进行解释.
应明确指定每个元素的类型(例如,人员、软件系统、容器或组件)。 每个元素都应该有一个简短的描述,以提供关键职责的“一目了然”的视图。 每个容器和组件都应该有明确指定的技术.
每条线都应该代表一个单向关系。 每一行都应该被标记,标记与关系的方向和意图一致(例如依赖或数据流)。尝试尽可能具体地使用标签,最好避免使用“使用”等单个词。 容器之间的关系(通常代表进程间通信)应该有明确标记的技术/协议.
C4模型图表绘制完成后,可以通过Review Checklist 进行自查,检查是否有不规范之处。Review Checklist被制成网页,可以通过 https://c4model.com/review/ 进行访问.
关于C4模型的架构图的绘制,一般有两种方式:
第一种是采用绘图工具,这类工具直接拖拽元素、调整样式,即可产出图片,例如draw.io、PPT等工具。绘图工具的优点是非常灵活,可以满足很多细节需求;缺点是通常调整元素的样式会比较繁琐.
第二种是采用基于文本的绘图工具,根据一定的语法去描述图片元素,最后根据文本自动渲染成图片,例如PlantUML。基于文本的绘图工具的优点是绘图快捷,只要根据语法写出描述文件,即可渲染出来,元素的样式已经默认调试好;缺点是样式不一定符合我们的审美,调整不方便.
本文着重讲解第二种,即基于文本的绘图工具.
基于文本的绘图工具有很多,例如:structurizr、PlantUML、mermaid,分别有自己的语法.
工具 | 语法 | 使用方式 | 地址 |
---|---|---|---|
structurizr | DSL | 提供Web界面渲染图片,并且可以生成C4-PlantUML和mermaid的代码 | https://structurizr.com/ |
C4-PlantUML | PlantUML | VS Code插件、IntelliJ Idea插件 | https://github.com/plantuml-stdlib/C4-PlantUML |
mermaid | mermaid | Markdown插件,提供Live Editor | https://mermaid.js.org/syntax/c4c.html , Mermaid Live Editor |
由于IntelliJ Idea、VS Code目前在开发者中非常普及,我们选择使用C4-PlantUML,结合VS Code和IntelliJ Idea分别进行C4模型的绘制.
VS Code环境的安装,见3.2.
IntelliJ Idea环境的安装,见3.3 。
直接官网下载安装即可,过程略去.
在VS Code的Extensions窗口中搜索PlantUML,安装PlantUML插件.
安装完PlantUML之后,为了提高效率,我们最好安装PlantUML相关的代码片段.
打开VS Code菜单,层级为Code→Preferences→User Snippets,如下图:
在选择Snippets File Or Create Snippets弹窗中,选择New Global Snippets file,如下图:
在接下来的弹窗中,输入Snippets file的文件名,如下图:
使用浏览器打开以下链接,并将浏览器返回的文本内容粘贴到VS Code编辑区 。
https://github.com/plantuml-stdlib/C4-PlantUML/blob/master/.vscode/C4.code-snippets 。
如图:
如果图形渲染出现问题,提示安装graphviz库,直接到graphviz官网安装即可。官网链接如下:
https://graphviz.gitlab.io/download/ 。
Mac系统推荐采用MacPorts安装.
通过以下链接,下载IntelliJ live template.
https://github.com/plantuml-stdlib/C4-PlantUML/blob/master/intellij/c4_live_template.zip 。
通过菜单路径 File | Manage IDE Settings | Import Settings ,选择下载的 ZIP文件, c4_live_template.zip ,导入并重启Idea即可.
C4-PlantUML的详细语法可以到官网github项目主页( https://github.com/plantuml-stdlib/C4-PlantUML )去了解,在此只做简单介绍.
以某招聘APP服务端架构图(Container级)为例子进行讲解,以下是渲染出来的效果图.
以下是完整plantuml代码:
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
!define SPRITESURL https://raw.githubusercontent.com/rabelenda/cicon-plantuml-sprites/master/sprites
!define DEVICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons
!define DEVICONS2 https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons2
!define FONTAWESOME https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/font-awesome-5
!include DEVICONS/java.puml
!include DEVICONS/mysql.puml
!include DEVICONS2/spring.puml
!include DEVICONS2/redis.puml
!include DEVICONS2/android.puml
!include DEVICONS2/apple_original.puml
title 招聘APP架构图(Container)
Person(P_User, "找工作的APP用户(应聘者)")
System_Boundary(Boundary_APP, "招聘APP系统边界"){
Container(C_ANDROID, "安卓移动端", "android", "移动APP安卓端",$sprite="android")
Container(C_IOS, "iOS移动端", "iOS", "移动APP iOS端",$sprite="apple_original")
Container(C_GATEWAY, "HTTP网关", "Netty", "鉴权、协议转换",$sprite="java")
Container(C_GATEWAY_CACHE, "网关缓存", "Redis", "缓存认证凭据",$sprite="redis")
Container(C_BFF, "BFF网关", "Spring Boot","整合后端接口",$sprite="spring")
Container(C_CERT, "实名认证服务", "Spring Boot", "内部实名认证服务",$sprite="spring")
Container(C_BIZ_1, "职位服务", "Spring Boot", "发布、搜索职位",$sprite="spring")
Container(C_PAYMENT, "支付服务", "Spring Boot", "内部支付服务",$sprite="spring")
ContainerDb(CDB_MYSQL, "职位信息数据库", "MySQL", "持久化职位信息",$sprite="mysql")
}
System_Ext(OUT_S_CERT, "实名认证服务","对用户进行姓名身份证号实名认证")
System_Ext(OUT_S_PAYMENT, "第三方支付服务","支持用户使用多种支付方式完成支付")
Rel(P_User, C_ANDROID, "注册登陆投递简历")
Rel(P_User, C_IOS, "注册登陆投递简历")
Rel(C_ANDROID, C_GATEWAY, "请求服务端","HTTPS")
Rel(C_IOS, C_GATEWAY, "请求服务端","HTTPS")
Rel_L(C_GATEWAY, C_GATEWAY_CACHE, "读写缓存","jedis")
Rel(C_GATEWAY, C_BFF, "将HTTP协议转为RPC协议","RPC")
Rel(C_GATEWAY, C_BIZ_1, "将HTTP协议转为RPC协议","RPC")
Rel(C_GATEWAY, C_PAYMENT, "将HTTP协议转为RPC协议","RPC")
Rel(C_BFF, C_CERT, "通过BFF处理之后,对外暴露接口服务","RPC")
Rel(C_BIZ_1, CDB_MYSQL, "读写数据","JDBC")
Rel(C_CERT, OUT_S_CERT, "对接外部查询实名信息接口","HTTPS")
Rel(C_PAYMENT, OUT_S_PAYMENT, "对接外部支付系统","HTTPS")
left to right direction
SHOW_LEGEND()
@enduml
PlantUML文件以puml作为文件扩展名.
整个文档由 @startuml 和 @enduml 包裹,是固定语法.
@startuml
@enduml
PlantUML中使用单引号(即 ' )作为注释标识.
首先是C4各个视图的include语句,以下语句代表引入了C4的Context、Container、Component视图.
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
其次是图标库:
!define SPRITESURL https://raw.githubusercontent.com/rabelenda/cicon-plantuml-sprites/master/sprites
!define DEVICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons
!define DEVICONS2 https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons2
!define FONTAWESOME https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/font-awesome-5
!include DEVICONS/java.puml
!include DEVICONS/mysql.puml
!include DEVICONS2/spring.puml
!include DEVICONS2/redis.puml
!include DEVICONS2/android.puml
!include DEVICONS2/apple_original.puml
注意这里有一个define语法,先通过 !define 定义一个标识,之后使用该标识的地方都会被替换 。
!define DEVICONS2 https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons2
!include DEVICONS2/spring.puml
‘ 等价于 !include https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons2/spring.puml
使用图标时,只需要在元素的声明语句中加入 $sprite="xxx" 即可.
ContainerDb(CDB_MYSQL, "职位信息数据库", "MySQL", "持久化职位信息",$sprite="mysql")
Person:系统的用户,可能是人或者其他系统 。
System:代表即将建设的系统,通常渲染为蓝色方块。 System_Ext:代表已存在的系统,通常渲染为灰色方块。 System_Boundary:某系统展开为容器时,则将System改为System_Boundary,代表系统的边界,内部放置容器元素,通常渲染为虚线框.
Container:待建设的容器,通常渲染为蓝色方块。 Container_Ext:已建设容器,通常渲染为灰色方块。 Container_Boundary:某容器展开为组件之后,则将Container改为Container_Boundary,代表容器的边界,内部放置组件元素,通常渲染为虚线框.
ContainerDb:待建设数据库,通常渲染为蓝色圆柱。 ContainerQueue:待建设消息队列,通常渲染为水平放置的蓝色圆柱.
Component:待建设组件,通常渲染为蓝色方块。 Component_Ext:已建设组件,通常渲染为灰色方块.
静态元素的语法为:
Container(alias, "label", "technology", "description")
alias:是图内元素的唯一ID,其他地方可以通过alias进行引用,比如在 Rel 中引用 label:代表元素的显示名称 technology:代表元素采用的核心技术,包括但不限于开发语言、框架、通信协议等 description:代表元素的简单描述 。
对于System_Boundary和Container_Boundary,则只需要alias和label,大括号内是该元素边界内的子元素.
Container_Boundary(alias, "label"){
}
Rel代表两个元素之间的关系,其语法为:
Rel(from_alias, to_alias, "label", "technology")
from_alias是起点元素的别名,to_alias是终点元素的别名,label则用来说明这个关联关系,technology代表采用的技术、通信协议。例如:
Rel(C_IOS, C_GATEWAY, "请求服务端","HTTPS")
代表iOS客户端通过请求网关接口访问服务端资源,采用HTTPS的通信方式.
建议在绘制 Rel 时标注出 technology .
C4-PlantUML提供了多种自动布局方案,我们可以根据实际需要进行选择.
left to right direction
是PlantUML的语法,也可以直接用。 通过 SHOW_LEGEND() 添加图例.
最后此篇关于手把手教你用代码画架构图的文章就讲到这里了,如果你想了解更多关于手把手教你用代码画架构图的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我尝试理解[c代码 -> 汇编]代码 void node::Check( data & _data1, vector& _data2) { -> push ebp -> mov ebp,esp ->
我需要在当前表单(代码)的上下文中运行文本文件中的代码。其中一项要求是让代码创建新控件并将其添加到当前窗体。 例如,在Form1.cs中: using System.Windows.Forms; ..
我有此 C++ 代码并将其转换为 C# (.net Framework 4) 代码。有没有人给我一些关于 malloc、free 和 sprintf 方法的提示? int monate = ee; d
我的网络服务器代码有问题 #include #include #include #include #include #include #include int
给定以下 html 代码,将列表中的第三个元素(即“美丽”一词)以斜体显示的 CSS 代码是什么?当然,我可以给这个元素一个 id 或一个 class,但 html 代码必须保持不变。谢谢
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我试图制作一个宏来避免重复代码和注释。 我试过这个: #define GrowOnPage(any Page, any Component) Component.Width := Page.Surfa
我正在尝试将我的旧 C++ 代码“翻译”成头条新闻所暗示的 C# 代码。问题是我是 C# 中的新手,并不是所有的东西都像 C++ 中那样。在 C++ 中这些解决方案运行良好,但在 C# 中只是不能。我
在 Windows 10 上工作,R 语言的格式化程序似乎没有在 Visual Studio Code 中完成它的工作。我试过R support for Visual Studio Code和 R-T
我正在处理一些报告(计数),我必须获取不同参数的计数。非常简单但乏味。 一个参数的示例查询: qCountsEmployee = ( "select count(*) from %s wher
最近几天我尝试从 d00m 调试网络错误。我开始用尽想法/线索,我希望其他 SO 用户拥有可能有用的宝贵经验。我希望能够提供所有相关信息,但我个人无法控制服务器环境。 整个事情始于用户注意到我们应用程
我有一个 app.js 文件,其中包含如下 dojo amd 模式代码: require(["dojo/dom", ..], function(dom){ dom.byId('someId').i
我对“-gencode”语句中的“code=sm_X”选项有点困惑。 一个例子:NVCC 编译器选项有什么作用 -gencode arch=compute_13,code=sm_13 嵌入库中? 只有
我为我的表格使用 X-editable 框架。 但是我有一些问题。 $(document).ready(function() { $('.access').editable({
我一直在通过本教程学习 flask/python http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-wo
我想将 Vim 和 EMACS 用于 CNC、G 代码和 M 代码。 Vim 或 EMACS 是否有任何语法或模式来处理这种类型的代码? 最佳答案 一些快速搜索使我找到了 this vim 和 thi
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve this
这个问题在这里已经有了答案: Enabling markdown highlighting in Vim (5 个回答) 6年前关闭。 当我在 Vim 中编辑包含 Markdown 代码的 READM
我正在 Swift3 iOS 中开发视频应用程序。基本上我必须将视频 Assets 和音频与淡入淡出效果合并为一个并将其保存到 iPhone 画廊。为此,我使用以下方法: private func d
pipeline { agent any stages { stage('Build') { steps { e
我是一名优秀的程序员,十分优秀!