软件架构模型 - C4 模型

最后更新:2021-02-01

1. C4模型

软件开发是一个团队协作的工作,混乱的理解会造成架构的无意义腐化、技术债的无意识积累、维护成本的无价值上升。

含糊不清的软件架构图容易导致误解,这可能会拖慢一个优秀团队的前进步伐。在我们的行业中,我们真的应该努力创建出更好的软件架构图。

C4模型的作者在多年的咨询中经常发现,很多个人画出来的架构图都是不一样的,但也不是说谁画错了,而是每个人的抽象层次不一样。抽象层次这种东西,说起来好像存在,但真要说清楚还挺难,于是作者类比地图,提出了缩放的概念。

上面的四张地图就是想说明,当我们看待真实世界的“架构图”时候,也是要不停的缩放,在每一个层次刻意忽略一些细节才能表达好当前抽象层次的信息。所以C4模型类比着把架构也提出了四个抽象层次:

从上到下依次是系统上下文Context、容器Container、组件Component和代码Code。基于这四个层次的抽象,C4模型由4张核心图和3张附属图组成。

在C4模型中软件系统被抽象为四个部分:(人)Person、(软件系统)Software System、(容器)Container 和 (组件)Component。

  • 人(Person)

这一抽象的提前是软件系统中“人”的部分与”物“分离,例如角色,或者外部子系统等。

  • 软件系统(Software System)

软件系统是最高级别的抽象,它描述了一些可以为用户带来价值的东西,无论他们是不是“人”。这包括您正在建模的软件系统,以及该软件系统所依赖的其他软件系统。

  • 容器(Container)

容器代表托管代码或数据的事物。为了使整个软件系统正常工作,必须运行一个容器。实际上,容器类似于服务器端的应用程序、一个手机APP,又或者是一个数据库。

  • 组件(Component)

“组件”一词在软件开发行业中是一个非常重载的术语,但是在这种情况下,组件只是封装在定义良好的接口后面的一组相关功能。如果您使用的是Java或C#之类的语言,则想到组件的最简单方法是它是接口后面的实现类的集合。这些组件的打包方式(例如,每个JAR文件,DLL,共享库中的一个组件与许多组件)的打包方式是一个单独且相互关注的问题。

2. 4张核心图

2.1. 系统上下文图

这是一个简单的图,可以告诉我们,要构建的系统是什么;它的用户是谁,谁会用它,它要如何融入已有的IT环境。这个图的受众可以是开发团队的内部人员、外部的技术或非技术人员。即:

  • 构建的系统是什么
  • 谁会用它
  • 如何融入已有的IT环境

在这一层级中细节并不重要,只需要显示系统概况。 重点应该放在人员(角色)和软件系统上,而不是技术,协议和其他低层级细节上,从而使非技术人员也能够看得懂。这个图也是明确需求的重要图示。

怎么画

中间是自己的系统,周围是用户和其它与之相互作用的系统。这个图的关键就是梳理清楚待建设系统的用户和高层次的依赖,梳理清楚了画下来只需要几分钟时间。

如上图所示,这个图表达的是你所开发的系统和它的用户以及它所依赖的系统之间的关系。从这个图上我们已经看出来C4图形的几个关键图形

C4说穿了就是几个要素:

  • 关系——带箭头的线
  • 元素——方块和角色
  • 关系描述——线上的文字
  • 元素的描述——方块和角色里的文字
  • 元素的标记——方块和角色的颜色、虚线框

通过在不同的抽象层次上,重新定义方块和虚线框的含义来将我们的表达限制在一个抽象层次上,从而避免在表达的时候产生抽象层次混乱的问题。

那么在系统上下文图里,方块指代的是软件系统,蓝色表示我们聚焦的系统,也就是我开发的系统(也可能是我分析的系统,取决于我是谁),灰色表示我们直接依赖的系统,虚线框表示的是企业的边界。通过这些图形化的元素表达我们可以看出来各个系统彼此之间的关系。

2.2. 容器图

当我们放大一个系统,就会看到容器,如上图所示,C4模型认为系统是由容器组成的。所谓容器,既不是Docker的容器,也不是JavaEE里的容器,而是借用了进程模型,代指有自己独立进程空间的一种存在。不管是在服务器上的单独进程空间,还是在浏览器里的单独进程空间,只要是单独的进程空间就可以看作一个容器。当然如果你容器化做得好,Docker的Container和这个Container可以一一对应。有了这个概念的存在我们就可以更清晰的去表达我们的架构,而不是总是用一些模糊的东西。

这个图的受众可以是团队内部或外部的开发人员,也可以是运维人员。用途可以罗列为:

  • 展现了软件系统的整体形态
  • 体现了高层次的技术决策
  • 系统中的职责是如何分布的,容器间的是如何交互的
  • 告诉开发者在哪里写代码

怎么画

用一个框图来表示,内部可能包括名称、技术选择、职责,以及这些框图之间的交互,如果涉及外部系统,最好明确边界。

注意:此图不需要表示非功能需求(怎么部署部署、实现高并发、高可用等)。

2.3. 组件图

当我们放大一个容器,我们就会看到组件,如上图所示。组件在这里面很好的把接口和它的实现类打包成一个概念来表达关系。比起模糊的堆砌在一起的文字,这种表达要清晰的很多,哪怕我的理解是不对的,也容易指出和讨论。

这个图主要是给内部开发人员看的,怎么去做代码的组织和构建。其用途有:

  • 描述了系统由哪些组件/服务组成
  • 厘清了组件之间的关系和依赖
  • 为软件开发如何分解交付提供了框架

2.4. 代码图

代码图就是UML里的类图之类很细节的图。一般是不画的,都是代码生成出来。除非非常重要的且还没有写出代码的组件才画代码图。

以上就是C4的核心图,我们可以看到四种不同的抽象层次的定义会让我们更容易固定住我们讨论的层次。

3. 三张扩展图

架构设计设计要考虑的维度很多,仅四张核心图是不够的,所以C4模型又提供了三张扩展图,可以让我们关注更多的维度

3.1. 系统景观图

系统景观图是比上下文图更丰富的系统级别的表达。不像上下文图只关注聚焦系统和它的直接关系,连一些间接相关的系统都会标示出来,那些系统的用户以及用户之间的关系也会标示出来,只是内部的用户会用灰色标记。

这个图有什么用呢?在我们分析一个企业的时候,我们需要一个工具帮助我们把一家公司给挖个底掉,做到完全穷尽,才能看到企业的全景图,从而理解局部的正确定位以做好局部设计为全局优化服务。使用这个图可以用来探索企业的数字化全景图。

3.2. 动态图

动态图不同于其他表达静态关系的图,它是用来表达动态关系的,也就是不同的元素之间是如何调用来完成一个业务的。所以动态图不仅仅适用于一个层面上,它在系统级、容器级和组件级都可以画,表达的目标是不一样的

3.3. 部署图

前面的几张图都是站在开发的角度思考,但是一个没有充分思考过部署的架构很容易变成一个运维的灾难。所以C4模型提供了一个部署图。这个图可以变成很好的Dev和Ops之间沟通的桥梁。

图上最大的的实线框不同于虚线框,它表达的是数据中心,当你开始考虑异地载备的时候它就有了意义。数据的同步、实例的数量都会影响部署图的内容。部署图基本都是容器级的,它能很好的表达出来容器到底部署了几个实例,部署在什么样的操作系统上,一个节点部署了几个容器之类。

4. 符号

C4 模型没有预定义任何特定的符号,你在这些示例图中看到的是一个个简单的符号,适用于白板、纸张、便签、索引卡片和各种图表工具。你也可以使用 UML 作为符号,并适当使用包、组件和原型。无论你使用哪种符号,每个元素都包应该含名称、元素类型(即“人”、“软件系统”,“容器”或“组件”)、技术选型(如果有的话),以及一些描述性文字。在图表中包含如此多的文本可能看起来很不寻常,但这些附加文本有助于消除软件架构图中通常会出现的不明确的表示。

即使符号对你来说是显而易见的,仍然要确保为这些符号提供图例。图例中应该包括颜色、形状、首字母缩略词、线条样式、边框、尺寸等。理想情况下,符号应该在每个细节层次上保持一致。下面是前面显示的容器图的图例。

5. 工具

https://structurizr.com/

http://draw.io/

6. 参考资料

https://www.infoq.cn/article/C4-architecture-model/

https://c4model.com/

Edgar

Edgar
一个略懂Java的小菜比