如何画好一张架构图?(内含知识图谱)

导读:架构图是什么?为什么要画架构图?如何画好架构图?有哪些方法?本文从架构的定义说起,分享了阿里文娱高级技术专家箫逸关于画架构图多年的经验总结,并对抽象这一概念进行了深入地讨论。内容较长,同学们可收藏起来细细阅读。

什么是架构图?

如何画好一张架构图,要做好这件事情首先要回答的就是什么是架构图。我们日常工作中经常能看到各种各样的架构图,而且经常会发现大家对架构图的理解各有侧重。深入追究到这个问题,可能一下子还很难有一个具象的定义,如果我们把这个问题进行拆分,理解起来就会容易一点。

架构图 = 架构 + 图

按照这个等式,我们可以把问题转换:

  • 架构是什么?
  • 图是什么?

图是什么?这个比较容易回答,图是一种信息的表达方式,所以架构图,即表达“架构”的图,也就是一种架构的表达方式。也即:架构图=架构的表达=表达架构的图

按照这种思路我们需要回答:

  • 什么是架构?要表达的到底是什么?
  • 如何画好一张架构图?

接下来的内容基本上就是按照这两个维度来做分析。

 

 

什么是架构?要表达的到底是什么?

Linus 03 年在聊到拆分和集成时有一个很好的描述:

I claim that you want to start communicating between independent modules no sooner than you absolutely HAVE to, and that you should avoid splitting things up until you really need to, because that communication complexity often swamps the complexity of the actual pieces involved in it.(让我们认识到一种现象,把复杂系统拆分成模块,似乎并没有降低整个系统的复杂度。它降低的只是子系统的复杂度。而整个系统的复杂度,反而会由于拆分后的模块之间,不得不进行交互,变得更加复杂。)

我理解这里描述的系统拆分就是架构的过程,基本出发点是为了效率,通过架构的合理拆分(无论是空间还是时间上的拆分)最终目的让效率最大化。那到底什么是架构,其实没有完全统一且明确的定义,如下三个定义可以参考。

在百度百科上的定义:

架构,又名软件架构,是有关软件整体结构与组件抽象描述,⽤于指导⼤型软件系统各个方面的设计。

在 Wikipedia 上的定义:

Architecture is both the process and the product of planning, designing, and constructing buildings or any other structures.

ISO/IEC 42010:20072 中对架构有如下定义:

The fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution. 

 

 

这三个定义也是见仁见智,但是我们基本可以得出:架构体现的是整体结构和组件之间的关系。

1. 架构的本质

这里引用三个观点来探讨架构的本质:

  • 架构的本质是为了管理复杂性;
  • 架构的本质就是对系统进行有序化重构,不断减少系统的“熵”,使系统不断进化;
  • 架构的本质就是对系统进行有序化重构,以符合当前业务的发展,并可以快速扩展。

上述三个观点提到的内容,基本表达了架构的核心目的:管理复杂性,效率最大化。以及架构的两个主要变化来源:一个是以改善软件质量为目的的内在结构性变化;另外一个是以满足客户需求为目的的外在功能性变化。

无论是何种变化,在我看来架构都是在不断的判断和取舍,在业务需求和系统实现之间做权衡,从而来应对未来变化的不确定性,如下图可以比较粗浅直观的表达这种理解。

 

 

2. 要表达的是什么?

在 EA 架构领域,有两种常见架构方法 RUP 和 TOGAF,这两个框架也是我们常常了解架构分类的两个维度。从个人的角度,我自己觉得 TOGAF 的分类方式更加广泛使用(如下右图)。

 

 

结合日常的业务开发,其实我们更多的是关注业务架构和应用架构,所以把上边的表达式进一步的拆解,在回答如何画好一张架构图之前,我们需要关注业务架构和系统架构,讨论清楚如何进行业务架构和系统架构。

 

 

3. 架构的过程其实就是建模的过程

我们都知道现实世界到软件世界或者面向对象的世界的过程,是一个不断抽象的过程,这其中的方法就是不断的建立模型。从现实世界到业务模型,从业务模型到概念模型,从概念模型到设计模型,通过不断的抽象去粗取精,形成对现实世界的层层抽象,所以架构的过程其实就是建模的过程。至此,我们有必要了解一下什么是建模。

百度百科定义:

建模就是建立模型,就是为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。

《Thinking in UML》定义:

建模(Modeling),是指通过对客观事物建立一种抽象的方法用以表征事物并获得对事物本身的理解,同时把这种理解概念化,将这些逻辑概念组织起来,构成一种对所观察的对象的内部结构和工作原理的便于理解的表达。

从上述两个定义上基本可以了解到建模就是抽象,对业务或现实世界的抽象,虽然不足以帮我们理解架构本身,但是可以将我们上述关注的业务架构和系统架构进一步向下 Down 一层,架构的过程是建模的过程,我们转换成两个简单的问题:模是什么?如何建?

4. 模是什么?如何建?

这是两个比较容易陷入理论性的问题,我们跳出来从结果看过程。接下来通过已经产出的一些架构图来反向看这些架构图是如何产出的,同时来回答这两个问题。

 

 

1)业务建模

回到当下业务本身,对我而言也是全新的,在最初接触的时候凭仅有的行业背景去理解,结合了大量的文档阅读最终产出了如下图示的《业务核心流程图》和《业务功能模块图》。这两张图基本上就涵盖了所有的业务内容。左边的业务流程图得到了这个行业 20 多年从业经验专家认可,他认为这就是 20 多年所从事的业务内容。

 

 

图片源于网络,为示意图,侵删

回溯整个过程,特别是左侧的业务核心流程图,今天我们看这张流程图很容易构架起一个基本逻辑来,纵向是不同的业务角色和系统,横向是时间的推进,特别容易理解。但我想说最开始的理解和分析是极其耗时和压力极大的过程。这个过程中我所用的方法就是:

  • “把书读厚”:大量的信息输入,同时探求可能性;
  • “把书读薄”:归类汇总,形成大图;
  • 逻辑对照,确保理解和分析的正确性。

把书读厚:

下图基本涵盖“把书读厚”的过程,汇聚大量的文档信息,尝试用多维度去形成逻辑。这个维度可能是依据历史经验,也可能是依据文档内容,比如在形成业务大图的过程中,我曾按可能的场景逻辑、可能的系统或领域逻辑分别把多个文档中的内容归类,探求可能性。

这个过程会很枯燥,特别是涉及一些业务的术语内容,理解起来就会很困难。我的方式就是把自己当做一名“探索者”,如同我们玩游戏一样,常常问自己“我的游戏地图全部点亮了吗?”未必要照顾到所有细节,但是需要力求覆盖整体内容。仔细想想,似乎也和日常的读书类似,这期间值得注意的是:

  • 重点关注一些业务概念被界定的地方、一些与自己逻辑推理有出入的地方;
  • 不断的调整自己阅读过程中记录的维度,矫正自己的分析方向;
  • 老老实实用文档中的原话来记录和呈现(这点很重要,特别是阅读英文材料,最好原汁原味的记录,有助于提升自己的专业性)。

 

 

把书读薄:

这个时候的重点是建立“大局观”,尝试梳理自己的逻辑主线,常规逻辑上讲都会划分为横纵,或者矩阵式的框架,当然这需要建立在前期的理解和分析上,这里常常隐含一个最最重要的假设:系统一定是给人用的,一定是解决客户问题的,否则毫无存在的意义。所以核心的套路是:谁?用什么样的服务/功能/能力?解决什么样的问题?从而刻画出:参与者角色、系统能力、交互关系,需要常常问自己的是:边界是什么?输入输出是什么?逐步的通过用例来梳理出业务功能,形成角色—>主流程—>分支流程,进而通过不断的归纳演绎形成最终的业务抽象描述“一张图”。

一个小的细节是不能妄图通过这些过程迅速在大脑里完成大图的绘制,还是需要从小的环节做起,把一部分小的业务闭环做成一个个的小组块,不要让它再占用大脑的空间,然后逐步的整体思考和把握,渐进式的形成大图;与此同时,大图的样式美观先完全忽略,走通逻辑再细致调整。之所以强调这个细节,是因为尝试通过“一张图”去描述一个非常大的业务本身就是件很有挑战的事情,如果不这么做容易让自己变得焦虑和急躁,这是一个慢功夫,需要耐心,需要在关键阻塞的地方慢下来,甚至一遍一遍的反复才能最终完成。

 

 

逻辑对照:

这是一个闭环封装的过程,把前期“读厚”过程中的记录,一些逻辑细节、关键流程都要逐一放到大图里去对照验证,确保业务理解的完整性和准确性,确保业务抽象能够覆盖所有已知的业务用例,甚至能够支持可能的业务场景。这个环节也是必不可少的部分。

总结一下业务建模(如下图),通过上述三个主要的过程,我们基本可以产出一些业务架构的大图、框图、流程图、用例图等等,是什么样的图并不重要,重要的是这个图面对的是谁?主要用来做什么?我后边也会讲到画图角度的问题。从我们目前的业务场景上看,业务架构图的核心目的是统一共识、减少沟通成本,无论是项目中的哪个角色大家都能讲一样的话,描述一样的事情。这就是建立对话能力和对话语境,特别是有大量外部客户的时候,一方面体现我们自己专业性很重要,另外一方面这种与客户对话的能力更重要,这也是上文中提到为什么要尽可能用原汁原味的文字去呈现一张图的目的。

 

 

2)系统建模

通过业务建模完成了从现实世界到业务模型的构建,在此基础上,如何通过抽象完成业务模型到设计模型的映射,这是系统建模要解决的问题。从研发实现的角度,这个阶段会产出各种各样的模型图,比如实体模型图、时序图、状态图、各个层次的架构图等等,但是无论何种角度,何种层次,系统建模一定是在业务建模的基础上,完成业务需求到系统模型之间的映射;这其中涉及业务功能到系统能力、业务流程到数据流程的映射;系统建模更强调职责、依赖、约束关系,用于指导研发的落地实现。

抛开具体的时序图、状态图不谈,简单看一下如下几个维度的架构图:

 

 

图片源于网络,为示意图,侵删

上述几张图的视角、层次和面向用户各不相同,基本上都能看到整体,但是细节程度不同,侧重表达的信息也完全不同。那么系统建模时应该如何去做呢,这个过程中我常常用的方法是(不尽然如此):

  • “剥洋葱式”的由大到小,由粗到细,覆盖所有已知和未来可能业务场景;善于利用各种模型表述:自然语言、关系模型、时序图、状态图、流程图、各种层次架构图等等进行模型表述,充分表达各种业务场景并不断验证;
  • 核心实体抽取:抓住核心概念,核心关系完成核心模型建立;
  • 终极武器:所有的设计/逻辑模糊的点,将所有已知场景分别套入,自己讲给自己。

“剥洋葱”:

在业务建模结果的基础上进行“剥洋葱”。这是一个不断拆解的过程,这个过程中的拆解非常重要的方式是就系统分工。如何分工?哪个模块负责什么?模块的输入和输出是什么?内部提供什么样的服务和能力?这几个问题在后文关于抽象的部分回答。一句话总结“剥洋葱”就是:从业务建模的“大局观”去按职责分工拆解成多个子系统、多个子模块、然后在模块能进行细分,层层剥解。

核心实体抽取:

关于核心实体的抽取,这里的关键问题是:哪些是实体?如何判断核心实体?如何抽取?抽取后的结果是什么样的?很难用一种方法论的形式去描述,我也没有完全形成我自己一成不变的方法论,但是我觉得如下三种方式可以供大家参考。

  • 对象的分析方法
实体(Entity):客观存在并可相互区别的事物称之为实体。实体可以是具体的人、事、物,也可以是抽象的概念或联系。

从这个概念理解,和我们面向对象万物兼对象的理解是基本一致的。所以实体的抽取也可以借鉴对象分析的方法:独立、可抽象、有层次性、在单个层次上又具备原子性。如下图是《Thinking in UML》中关于对象的分析方法。

 

 

  • 用例分析的方法

通过从业务用例中,去提取其中的关键词,不同的关键词可能表达了实体、关系、属性等等内容,从而完成模型分析与建立。这里引用六铢老师在《问题空间领域模型基本抽象方法》中的的内容,简述如下:

一句完整的用例描述中,首先找名词,以「主语」和「宾语」为主,这些名词基本可以确定我们的实体;其次找形容词,存在于「定语」和「状语」中,找到形容词基本可以确定对应属性的值;然后通过对用例的补充,细化,对名词进行定义,慢慢的,我们会得到我们的领域模型和对应的属性。最后通过动词&形容词(存在于【谓语】,【状语】,【定语】)来确定他们之间的关联关系。
  • 问题分析的方法

这是《聊聊架构》中提的方式,具体讲就是通过寻找问题的主体,然后分析主体的生命周期,进而通过区分生命周期里的关键活动来聚焦主体的关键属性和关键关系。推荐大家阅读前 9 章的内容,总计才 40 页的内容,可能会有所体会。这里举一个书中的例子:

一个笑话:一位女士对老公说:把袋子里的土豆削一半下锅;结果所有土豆都下锅了,而且每个土豆被削了一半。

作者指出,这里其实就没有清晰的设别主体,这个主体不单是土豆,而是隐含的人要吃土豆,包括人和土豆两个实体,这两个实体之间的关系就是要解决的业务场景:怎样吃?如何吃?为什么吃?所以主体识别不清楚,可能会导致整体实现的偏离。当然实际过程中不会犯这么愚蠢的错误,但是也侧面说明核心实体的抽取是非常关键的。

终极武器 - 自己讲给自己:

实际的业务开发中,往往一种业务设计实现要满足上层N个业务场景,这其中有共性也有个性化诉求,这个过程中我们很容易被多场景之间的异同搞混乱,要么逻辑不清晰、要么过度设计、要么考虑不周。我观察过很多同学包括我自己,在一定的业务复杂度时容易失去设计的焦点。我的做法与业务建模类似,一定要逻辑对照:在所有的设计/逻辑模糊的点,将所有已知场景分别套入,自己讲给自己。请注意这里是“分别套入”,在当前的设计层次下一个场景验证完再去验证下一个场景,找出阻塞的、模糊的点,重新梳理再优化设计。系统建模的结果指导我们软件设计实现,所以一定要反复梳理打通,这个反复的过程其实也是提升架构能力的过程,累积到一定程度就会自然通透。

回到开始的那个问题:

模是什么?通过上面业务建模和系统建模的描述,简单来讲模就是业务的映射,这个映射的结果是业务模型、概念模型或设计模型,但是所有的出发点都是业务需求:客户是谁?核心诉求是什么?

如何建?上面通过业务建模和系统建模两个维度,从个人实践角度大概讲了常规的套路,建模的本质其实一个抽象的过程,但是上述业务和系统建模抽象的过程其实还有两个问题并没有完全说清楚:

  • 业务建模中“把书读薄”归类汇总,建立「大局观」,形成大图,这里按什么维度去归类?如何判断归类是正确的?
  • 系统建模中“剥洋葱”怎么拆?按什么拆?上述架构图中的层次、领域如何划分层次?边界在哪里?

说回抽象

Haskell 语言的设计者之一 Paul Hudak 曾说过一句略带夸张的话:编程中最重要的三件事是:抽象,抽象,抽象

"abstraction, abstraction, abstraction"are the three most important things in programming.

如果要问程序员最重要的能力有哪些,我相信抽象一定是其中最重要的之一。那到底什么是抽象?

百度百科定义:

从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。

如果更精炼的概括:抽象就是做减法和做除法。通过舍弃非本质和无关紧要的部分,着眼于问题的本质,去粗取精;通过透过现象看本质,发现不同事物之间的共同之处,异中求同,同类归并,也就是做除法。上文中建模过程是共性抽象,通过不断的抽象达到某个状态为止,我理解这个状态没有确定性的答案,核心就是满足业务场景的需要,其实这背后也有一个边界的问题。

1. 抽象的角度

生活中处处都是抽象,但是我们似乎少了为什么是这样或那样抽象的思考。抽象是有角度之分的。

生活中我们常常说“我的观点是…”,其实这里的“观点”就是一个角度问题,从一定的立场或角度出发,对事物或问题所持的看法。以生活中的常见的实物来说(如下图),我们是否能快速的说出其中的相同点和不同点。

 

 

如图中已经标注的,我们从功用的角度对它们定义了椅子、桌子、凳子和柜子这样的区分,但显然很有很多很多角度,比如:物料、文字、高矮等等维度,从不同维度看过去,会有完全不同的相同点和不同点表述,所以,本质是什么?本质是:

  • 抽象角度其实也是分类的角度,角度不同,会导致完全不同建模方向和结果;
  • 抽象的角度就是建模的方向和目的(“屁股决定脑袋”)。

重新回到我们前边的两个问题,业务建模中我们谈到了归类,按什么去归类,答案呼之欲出,按我们的业务流程去归类、按客户的角色去归类,又回到了那个最初始的问题:客户是谁?核心诉求是什么?

同时,上文中我们提到,模是业务的映射,基于对抽象的理解,我们可以进一步展开:模是在确定抽象角度下的业务映射

 

 

2. 抽象的层次

Wikipedia 关于抽象的定义中有一个关于报纸的例子:

  1. 我的 5 月 18 日的《旧金山纪事报》
  2. 5 月 18 日的《旧金山纪事报》
  3. 《旧金山纪事报》
  4. 一份报纸
  5. 一个出版品

 

这五句话中,我们可以感受到抽象的层次,抽象层次越高,细节越少,普适性越强。再比如下图中关于网络模型的抽象,关于操作系统内核的抽象,我们可以明显的看到不同层次的抽象,就是过滤不同的信息,最终留下来的信息才是当前抽象层次所需要的信息。从系统设计实现上来说,抽象层次越高,越接近设计,越远离实现,同时抽象的模型越不受细节的羁绊,稳定性越高,普适性越强,可重用性就越高

 

 

那么这里抽象的划分层次的依据是什么?原则又是什么?我的经验是,划分抽象层次的依据主要包含两个:

  • 以抽象角度分层(可能一层是多角度的聚合)
  • 面对变化分层(用层次隔离变化)

其实这个也不能完全解释如何分层,原则是什么?我觉得这是几个最通用的原则

  • 公用的往下走
  • 个性的往上走
  • 下层可以独立于上层存在
  • 控制下层的变化

考虑抽象层次的好处是不论在哪一个层次上,我们只需要面对有限的复杂度,从而专心考虑这个层次上的抽象是什么,要表达的信息是什么。

3. 抽象的边界

除了角度、层次之外,我们还需要考虑的抽象的边界。如果说层次考虑的是纵向维度的表达,那么边界考虑的是横向维度的表达。如何确定边界,一个总的原则是按照职责进行划分,这里的职责其实也就是分工。一旦职责确定,我们在做建模分析时就不需要把整个业务大局放进来从头到尾去分析一遍,我们只需要考虑当前分工下的上游和下游即可,这样的信息量大大减少,自然的我们面对的领域复杂度也会降低到一定程度。

如果一定要给出边界的定义,我的理解是:边界是在确定抽象角度下,通过寻找核心的业务活动,抽取核心实体,进一步确定实体核心生命周期的结果。可能有一点点绕,关键词是:核心业务活动、核心实体、核心实体生命周期。

以现场娱乐行业为例,如下这张图包含了最高抽象层次下业务的全生命周期,这个抽象层次下的主体是什么,我的理解是票,项目生产的结果是票,分销或电商服务是对票的销售,现场是对票的核验,至此以票为核心实体的生命周期结束。

 

 

如果我们往下 Down 一层,从项目生产这一个业务活动去看,整个业务流程是这样:

项目管理->场馆座位分销->票房预测->场次管理->配额管理->绘座->票房规划

从生产这个视角去看,核心的实体不是票,而是场次(确定时间、确定地点、确定内容的一场演出或赛事),所有的关键业务活动都是以场次为维度,生产领域里需要考虑的主要就是场次的核心生命周期。

所以,在不同的抽象角度、不同的抽象层次,根据分工的不同会有不同的核心业务活动、不同的核心实体、边界的确定关键在寻找核心的生命周期。寻找生命周期的过程,就是发现内聚的过程;将所有关于生命周期的业务活动累积,就可以提升领域或模块的内聚性。

4. 抽象的评估

前边我们基本说清楚了抽象的角度、层次和边界,从三个维度确定了抽象的结果。那么如何评估抽象结果的好坏呢?答案是“高内聚,低耦合”,当然还有更多的原则,但是单从实践的角度,我觉得这是最最重要的。

· 耦合是软件结构中各模块之间相互连接的一种度量;
· 内聚是一个模块内部各成分之间相关联程度的度量。

“高内聚,低耦合”从内部、外部两个视角去评估抽象结果的好坏。这其中也有对应的原则和方法论,常规的套路是:

  • 每次从一个角度来切分,然后换多个角度来审视
  • 通过组合、拆分来精化、优化模型与设计(抽象的结果)
  • 关键的审视点:耦合性:减少模块间通信量;内聚性:功能单一化;变化的隔离性:减少信息依赖,建隔离层、虚拟层。

5. 抽象的方法论(套路)

我想,至此,我们说清楚了前面的那两个问题:

  • 业务建模中“把书读薄”归类汇总,建立“大局观”,形成大图,这里按什么维度去归类?如何判断归类是正确的?
  • 系统建模中“剥洋葱”怎么拆?按什么拆?上述架构图中的层次、领域如何划分层次?边界在哪里?

总结前面说的所有关于抽象的内容,形成抽象的方法论(套路)

  • 抽象有两种方法,一种是自顶向下,另一种是自底向上;
  • 业务建模,是从小到大,从局部到整体,自底向上的归纳、演绎的抽象过程;
  • 系统建模,是从大到小,从整体到局部,自顶向下的拆解、切分的抽象过程;
  • 但不绝对,自上而下和自下而上,往往在过程中是随意切换的。

下面这张图来自于《Thinking in UML》,我觉得这个循环的过程可以表达上面这四个点,供大家参考。

 

 

如何画好一张架构图?

回到主题,如果上边的问题说清楚了,接下来的事情就相对简单了。对于架构图是什么这个问题,我们可以把之前的等式进行延展:架构图 = 架构的表达 = 架构在不同抽象角度和不同抽象层次的表达,这是一个自然而然的过程。不是先有图再有业务流程、系统设计和领域模型等,而是相反,用图来表达抽象的思考和内容。

那么架构图有什么用?给谁看?回答这个问题需要讲清楚为什么要画架构图,同时也需要考虑一个问题就是:架构图是不是越多越好,越详细越好?

1. 画架构图是为了什么?

A picture is worth a thousand words (一图胜千言),从 Why 层面讲,我觉得就是如下两点:

  • 解决沟通障碍:达成共识、减少歧义;
  • 提升协作效率:团队内部和团队之间的协作、沟通、愿景和指导。

但是上述两点其实是非常笼统的信息,如果放在 What 层面,我们必须要考虑架构图面对的“客户”,不同的客户有不同的诉求(其实也就是角度和层次),在不同的抽象层次架构图所表达的信息内容可以完全不一样。以目前团队做的事情为例,架构图的目标客户至少有几类:

  • 参与项目的各团队各角色(业务、产品、开发、测试、安全、GOC)
  • 项目之外的客户(外部客户,外部评审专家)
  • 各层次 TL(汇报,跨 BU,跨团队协作沟通)

所以画架构图,我们必须首先明确沟通交流的目的和面向的客户,只有明确了这两个点才能更加有针对性的达成上边所说的那两点目标:解决沟通障碍,提升协作效率。

2. 怎么画?

1)先说分类

架构图分类,本质上是从不同的视角,不同的抽象角度去看,作出清晰、简化的描述,涵盖特点方面忽略无关方面。

从业务应用开发的维度,一般的抽象层次可以分为:

业务全域—>子域—>模块—>子模块—>包—>类—>方法

这其中:

  • 较低层次的抽象:应用内部包图、类图;某个领域:实体图、时序图、状态图、用例图等等;
  • 较高层次的抽象:具有一定的复杂性,比如微服务架构,系统间的交互图,领域/子领域架构图,整个系统架构图等等。

当然,还有很多其他的分类方式,比如:RUP 4+1,GOGAF9 等等分类方式。单从实践的角度,我觉得何种分类不是最重要的,最重要的是想清楚面向谁和解决什么诉求,然后思考架构图到底从哪个角度、哪个层次去抽象。我们目前所做的项目,有很时候要去和国外的业务专家、技术专家去沟通,大家也并没有一个明确的标准定义,表述清楚问题,达成共识这是最最关键的,至于架构图的粒度、类别、内容可以逐步的去完善,去粗取精,迭代优化。

2)再说构图

构图的部分,我们大家都用 UML 画过类图,涉及泛化、聚合、组合、依赖等等关系,分别用不同的虚实线、箭头样式进行表达。所以画架构图需要考虑架构图的组成元素,要保证符合一贯理解,架构图的组成元素可能涉及:

  • 方框、各种形状、虚实线、箭头、颜色(不同颜色代表什么意思)和文字内容;
  • 虚实线表达什么?组件类型,模块类型,层,服务,需要考虑是否已经实现等?不同状态的标识怎么传递?
  • 箭头表达什么?数据流或关联关系?
  • 交互类型可以是同步或异步的;关联类型可以是指依赖、继承、实现。

构图最最重要的是需要考虑内容术语一致性问题、碎片化问题、信息粒度大小的问题,以及图表的外观问题。

3. 如何评判架构图的好坏

架构图的好坏,我理解主要是两个方向,一个是需要跳出图本身去看,业务领域的抽象设计合理性,是否符合“高内聚,低耦合”的要求,这个需要回到前文的业务建模、系统建模和抽象过程去寻找答案。另外一个方向是图本身,以下几个点供参考:

  • 内容术语一致、信息粒度大小一致,图例清晰,颜色类型统一,美观;
  • 图中的信息与相应的抽象级别相关,且满足利益相关者(合作方)的需求;
  • 一张好的架构图不需要多余的文字解释!受众有没有准确接收到想传递的信息;如果它所导致的疑问比它能解释的问题还要多,那么它就不是一张好的架构图;
  • 架构图应该帮助每个人看到大局,了解周围的环境,适当的上下文信息;
  • 架构图应该避免“只见树木,不见森林”。

但是,终归还是那句话,“一张图片胜过千言万语”。不管好坏,不管是否美观,人是视觉动物,用图表达可以极大的提升沟通效率,先画起来吧!

最后也聊聊架构师

这是来自于阿白老师的文章《架构师到底是做什么的?》,越是琢磨,越觉得深以为然。其中提到了好的架构师的画像和不好的画像,如下图,与大家共勉。

 

 

从我个人的成长经历看,架构师很重要的一点要学会“权衡”,既要兼顾当下痛点也要符合未来一定时间的发展,既要保留未来的可扩展性也要避免过度设计。选择什么样的时间节点、什么样的业务场景以及什么样的架构迭代策略至关重要,这些决策的关键在于判断和取舍,需要结合深刻的业务思考乃至组织架构去做权衡落地。一点点不算经验的经验:

1. 快速学习

快不是一个速度问题,也是一个判断或者标准问题。面对一个全新业务场景,如何能够识别20%的关键业务路径,关键业务痛点,如何短时间把自己变成业务专家这是一个架构师基本的素质。我的一点经验就是要去「吸金式」的思考,带着问题主动思考,客户是谁?有什么诉求?需要解决什么样的问题?我们能提供什么样的价值?多问为什么?这也需要长时间的刻意训练。

2. 不要屁股决定脑袋

要跨角色、跨层级去看待业务问题,这个点容易陷入说教,说实话我自己做得也未必到位。但是时刻提醒自己的思考是否被局限,在哪一个维度,是 Have-do-be,还是 be-do-Have 等等;同时也不断的在提醒自己永远不要屁股决定脑袋。

3. 提升思考能力和对于技术原理或本质的理解

我觉得这是最底层的能力,业务开发中我觉得最大的两个难点:一是逻辑的复杂性,二是需求的变化性。我们不应该大部分时间花在寻找解决方案上,而应该花更多的时间在选择解决方案上。这就要求我们对业务全局、行业深度、技术视野、技术深度、业务共性、个性特征等等形成自己的认知。权衡取舍,取什么舍什么?该怎么取怎么舍?那个度在哪里?唯有思考,自驱,累积和坚持,勇猛精进,志愿无倦。

最后的最后

希望这篇文章对大家有帮助,附上最初在考虑这个主题时的构思过程及思考路径,供大家参考。

 

 

参考文档:

关注“阿里巴巴云原生”公众号,回复“架构”即可查看清晰知识大图!

课程推荐

为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

点击即可免费观看课程:developer.aliyun.com/le

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

写了多年的代码,始终觉得如何写出干净优雅的代码并不是一件容易的事情。按 10000 小时刻意训练的定理,假设每天 8 小时,一个月 20 天,一年 12 个月,大概也需要 5 年左右的时间成为大师。其实我们每天的工作中真正用于写代码的时间不可能有 8 个小时,并且很多时候是在完成任务,在业务压力很大的时候,可能想要达到的目标是如何尽快的使得功能 work 起来,代码是否干净优雅非常可能没有能放在第一优先级上,而是怎么快怎么来。

在这样的情况下是非常容易欠下技术债的,时间长了,这样的代码基本上无法维护,只能推倒重来,这个成本是非常高的。欠债要还,只是迟早的问题,并且等到要还的时候还要赔上额外的不菲的利息。还债的有可能是自己,也有可能是后来的继任者,但都是团队在还债。所以从团队的角度来看,写好代码是一件非常有必要的事情。如何写出干净优雅的代码是个很困难的课题,我没有找到万能的 solution,更多的是一些 trade off,可以稍微讨论一下。

代码是写给人看的还是写给机器看的?

在大部分的情况下我会认为代码是写给人看的。虽然代码最后的执行者是机器,但是实际上代码更多的时候是给人看的。我们来看看一段代码的生命周期:开发 --> 单元测试 --> Code Review --> 功能测试 --> 性能测试 --> 上线 --> 运维、Bug 修复 --> 测试上线 --> 退休下线。开发到上线的时间也许是几周或者几个月,但是线上运维、bug 修复的周期可以是几年。

在这几年的时间里面,几乎不可能还是原来的作者在维护了。继任者如何能理解之前的代码逻辑是极其关键的,如果不能维护,只能自己重新做一套。所以在项目中我们经常能见到的情况就是,看到了前任的代码,都觉得这是什么垃圾,写的乱七八糟,还是我自己重写一遍吧。就算是在开发的过程中,需要别人来 Code  Review,如果他们都看不懂这个代码,怎么来做 Review 呢。还有你也不希望在休假的时候,因为其他人看不懂你的代码,只好打电话求助你。这个我印象极其深刻,记得我在工作不久的时候,一次回到了老家休假中,突然同事打电话来了,出现了一个问题,问我该如何解决,当时电话还要收漫游费的,非常贵,但是我还不得不支持直到耗光我的电话费。

所以代码主要还是写给人看的,是我们的交流的途径。那些非常好的开源的项目虽然有文档,但是更多的我们其实还是看他的源码,如果开源项目里面的代码写的很难读,这个项目也基本上不会火。因为代码是我们开发人员交流的基本途径,甚至可能口头讨论不清楚的事情,我们可以通过代码来说清楚。代码的可读性我觉得是第一位的。各个公司估计都有自己的代码规范,遵循相关的规范保持代码风格的统一是第一步(推荐谷歌代码规范和微软代码规范)。规范里一般都包括了如何进行变量、类、函数的命名,函数要尽量短并且保持原子性,不要做多件事情,类的基本设计的原则等等。另外一个建议是可以多参考学习一下开源项目中的代码。

KISS (Keep it simple and stupid)

一般大脑工作记忆的容量就是 5-9 个,如果事情过多或者过于复杂,对于大部分人来说是无法直接理解和处理的。通常我们需要一些辅助手段来处理复杂的问题,比如做笔记、画图,有点类似于在内存不够用的情况下我们借用了外存。

学 CS 的同学都知道,外存的访问速度肯定不如内存访问速度。另外一般来说在逻辑复杂的情况下出错的可能要远大于在简单的情况下,在复杂的情况下,代码的分支可能有很多,我们是否能够对每种情况都考虑到位,这些都有困难。为了使得代码更加可靠,并且容易理解,最好的办法还是保持代码的简单,在处理一个问题的时候尽量使用简单的逻辑,不要有过多的变量。

但是现实的问题并不会总是那么简单,那么如何来处理复杂的问题呢?与其借用外存,我更加倾向于对复杂的问题进行分层抽象。网络的通信是一个非常复杂的事情,中间使用的设备可以有无数种(手机,各种 IOT 设备,台式机,laptop,路由器,交换机...), OSI 协议对各层做了抽象,每一层需要处理的情况就都大大地简化了。通过对复杂问题的分解、抽象,那么我们在每个层次上要解决处理的问题就简化了。其实也类似于算法中的 divide-and-conquer, 复杂的问题,要先拆解掉变成小的问题,从而来简化解决的方法。

KISS 还有另外一层含义,“如无必要,勿增实体” (奥卡姆剃刀原理)。CS 中有一句 "All problems in computer science can be solved by another level of indirection", 为了系统的扩展性,支持将来的一些可能存在的变化,我们经常会引入一层间接层,或者增加中间的 interface。在做这些决定的时候,我们要多考虑一下是否真的有必要。增加额外的一层给我们的好处就是易于扩展,但是同时也增加了复杂度,使得系统变得更加不可理解。对于代码来说,很可能是我这里调用了一个 API,不知道实际的触发在哪里,对于理解和调试都可能增加困难。

KISS 本身就是一个 trade off,要把复杂的问题通过抽象和分拆来简单化,但是是否需要为了保留变化做更多的 indirection 的抽象,这些都是需要仔细考虑的。

DRY (Don't repeat yourself)

为了快速地实现一个功能,知道之前有类似的,把代码 copy 过来修改一下就用,可能是最快的方法。但是 copy 代码经常是很多问题和 bug 的根源。有一类问题就是 copy 过来的代码包含了一些其他的逻辑,可能并不是这部分需要的,所以可能有冗余甚至一些额外的风险。

另外一类问题就是在维护的时候,我们其实不知道修复了一个地方之后,还有多少其他的地方还需要修复。在我过去的项目中就出现过这样的问题,有个问题明明之前做了修复,过几天另外一个客户又提了类似的问题出现的另外的路径上。相同的逻辑要尽量只出现在一个地方,这样有问题的时候也就可以一次性地修复。这也是一种抽象,对于相同的逻辑,抽象到一个类或者一个函数中去,这样也有利于代码的可读性。

是否要写注释

个人的观点是大部分的代码尽量不要注释。代码本身就是一种交流语言,并且一般来说编程语言比我们日常使用的口语更加的精确。在保持代码逻辑简单的情况下,使用良好的命名规范,代码本身就很清晰并且可能读起来就已经是一篇良好的文章。特别是 OO 的语言的话,本身 object(名词)加 operation(一般用动词)就已经可以说明是在做什么了。重复一下把这个操作的名词放入注释并不会增加代码的可读性。并且在后续的维护中,会出现修改了代码,却并不修改注释的情况出现。在我做的很多 Code Review 中我都看到过这样的情况。尽量把代码写的可以理解,而不是通过注释来理解。

当然我并不是反对所有的注释,在公开的 API 上是需要注释的,应该列出 API 的前置和后置条件,解释该如何使用这个 API,这样也可以用于自动产品 API 的文档。在一些特殊优化逻辑和负责算法的地方加上这些逻辑和算法的解释还是非常有必要的。

一次做对,不要相信以后会 Refactoring

通常来说在代码中写上 TODO,等着以后再来 refactoring 或者改进,基本上就不会再有以后了。我们可以去我们的代码库里面搜索一下 TODO,看看有多少,并且有多少是多少年前的,我相信这个结果会让你很惊讶(欢迎大家留言分享你查找之后的结果)。

尽量一次就做对,不要相信以后还会回来把代码 refactoring 好。人都是有惰性的,一旦完成了当前的事情,move on 之后再回来处理这些概率就非常小了,除非下次真的需要修改这些代码。如果说不会再回来,那么这个 TODO 也没有什么意义。如果真的需要,就不要留下这个问题。我见过有的人留下了一个 TODO,throw 了一个 not implemented 的 exception,然后几天之后其他同学把这个代码带上线了,直接挂掉的情况。尽量不要 TODO, 一次做好。

是否要写单元测试?

个人的观点是必须,除非你只是做 prototype 或者快速迭代扔掉的代码。

Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the "unit") meets its design and behaves as intended. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method. 

 From Wikipedia

单元测试是为了保证我们写出的代码确实是我们想要表达的逻辑。当我们的代码被集成到大项目中的时候,之后的集成测试、功能测试甚至 e2e 的测试,都不可能覆盖到每一行的代码了。如果单元测试做的不够,其实就是在代码里面留下一些自己都不知道的黑洞,哪天调用方改了一些东西,走到了一个不常用的分支可能就挂掉了。我之前带的项目中就出现过类似的情况,代码已经上线几年了,有一次稍微改了一下调用方的参数,觉得是个小改动,但是上线就挂了,就是因为遇到了之前根本没有人测试过的分支。单元测试就是要保证我们自己写的代码是按照我们希望的逻辑实现的,需要尽量的做到比较高的覆盖,确保我们自己的代码里面没有留下什么黑洞。关于测试,我想单独开一篇讨论,所以就先简单聊到这里。

要写好代码确实是已经非常不容易的事情,需要考虑正确性、可读性、鲁棒性、可测试性、可以扩展性、可以移植性、性能。前面讨论的只是个人觉得比较重要的入门的一些点,想要写好代码需要经过刻意地考虑和练习才能真正达到目标!

架构制图:工具与方法论

简介:软件工程也是工程,因此传统工程制图的一些基本理论,在软件行业同样适用。但另一方面,软件与实体制造业之间还是有着本质区别,所以在制图方面的需求和方式也大相径庭,无法直接套用。作为软件行业的从业者,你可以完全不懂工程制图,但你不得不懂架构制图 —— 这是任何程序员职业生涯的的必修课。

 

前言

“架构制图”这词乍一听似乎有些晦涩,但如果提起“工程制图”,相信绝大部分工科背景的程序员们都不会陌生,甚至还能共同感慨下那些年一起伏在宿舍左手圆规,右手直尺,徒手作图到深夜的日子。

软件工程也是工程,因此传统工程制图的一些基本理论,在软件行业同样适用。但另一方面,软件与实体制造业之间还是有着本质区别,所以在制图方面的需求和方式也大相径庭,无法直接套用。作为软件行业的从业者,你可以完全不懂工程制图,但你不得不懂架构制图 —— 这是任何程序员职业生涯的的必修课。

本文在后半段将介绍如何用图去描述(describe)和传达(communicate)你的架构设计。值得强调的是,本文并不会侧重于单一的方法和工具,而是更希望关注那些优秀方法背后的通用方法论,即架构制图的本质共性最佳实践。希望本文能起到引子作用,激发大家对自己日常工作中关于架构和制图部分的关注、审视与思考;如果还真能帮助大家提升一点点制图效率和效果,那就更好不过了。

什么是软件架构?

1. 软件架构定义

IEEE 给出的定义:架构是环境中该系统的一组基础概念(concepts)属性(properties),具体表现就是它的元素(elements)关系(relationships),以及设计与演进的基本原则(principles)

CMU 软件工程研究院的定义:架构是用于推演出该系统的一组结构(structures),具体是由软件元素(elements)、元素之间的关系(relationships),以及各自的属性(properties)共同组成。

Uncle Bob 在 Clean Architecture 一书中给出的定义:架构是创建者给予该系统的形态(shape)。这个形态的具体形式来源于对系统组件(components)划分排列,以及这些组件之间互相通讯的方式。

2. 架构核心要素

综合上述各种权威定义,软件系统的架构通常需要包含如下四类核心要素:

  • 元素(elements):将系统拆分为一组元素 - 模块、组件、结构体、子系统;
  • 关系(relationships):不同元素之间的关系 - 交互、依赖 、继承、组合、聚合;
  • 属性(properties):每个元素具备的属性 - 名称、职责、接口、实现限制等;
  • 原理(principles):为什么这么设计 - 拆分依据、设计原则、决策原因等。

为什么架构很重要?

1. 架构是系统实现的蓝图

最近有部很火的网剧叫《摩天大楼》,讲述了一段匪夷所思的悬疑故事。为什么扯这个呢?因为我想借用这个剧的标题来问个问题:摩天大楼是由谁建起来的?也许你心里会默念:废话,不就是建筑工人们一砖一瓦堆起来的嘛。仔细再想想?背后是不是还有一堆操碎了心的建筑设计师(比如剧中帅气的林大森)和土木工程师们?他们虽然不搬砖也不扛水泥,但如果没有他们产出的那些繁琐严谨的设计图纸,摩天大楼是是不可能像农村自建房一样仅凭工人们各自的经验与想象力就能快速平稳地竖立起来的。

正是靠着这些图纸所描绘出来的工程蓝图(blueprints),才让成百上千工人们的分工合作和验收标准有了依据:大家只需要照着蓝图,按部就班地把自己所负责的那些砖瓦添上去就行了;只要蓝图正确,且施工过程也没有偏差,最终顺利完工只是个时间问题。

与建筑、汽车或者任何其他工程行业一样,软件在落地实现(编码)之前也需要先有蓝图;而其中最重要的一份蓝图,就是架构设计。没有架构,仅凭程序员自己脑子里的模糊设想,也许你可以像传统手艺人一样独自创造出一些美好有用的小东西(比如 Linux 0.01 版本),但不太可能以工程的方式协同一个团队共同建造起一个与摩天大楼规模类似的复杂软件系统(比如现代的 Linux 系统)。一方面,人类的思维能力终归有限,必须依靠架构这种高度抽象和简化的蓝图,才能让复杂系统的创造、理解、分析和治理变得可行;另一方面,量级达到一定程度的大型系统,也只能依靠多人分工合作才能完成,而架构也正是多人沟通协作的重要基础。

2. 架构是沟通协作的基础

软件项目的最终价值产出就是软件系统,而架构作为软件系统的灵魂和骨架,可以起到如下作用:

  • 理解对齐:所有软件系统的目的都是为了实现用户需求,但实现的途径有无限种可能性(相比传统工程行业,软件的灵活性更大、知识迭代更快)。架构设计就是去选择其中一条最合适的实现途径,因此其中会涉及非常多关键的选路决策(为什么要这么拆分?为什么选择 A 技术而不是 B?)。这些重要的技术决策需要通过架构描述这种形式被记录和同步,才能让项目组所有成员对整个系统的理解对齐,形成共识
  • 工作量化:项目管理最重要的步骤之一就是工时评估,它是确定项目排期和里程碑的直接依据。显然,只通过 PRD / 交互图是无法科学量化出项目工作量的,因为很难直观判断出一句简短需求或一个简单页面背后,究竟要写多少代码、实现起来难度有多大。有了清晰明确的架构之后,理论上绝大部分开发工作都能做到可见可预测可拆解,自然而然也就能够被更准确地量化。当然,精准的工作量评估在 IT 行业内也一直是个未解之谜,实际的工期会受太多未知因素影响,包括程序员的技能熟练度、心情好不好、有没有吃饱等。
  • 标准术语:编程作为一种具有创造力的工作,从某种角度看跟写科幻小说是类似的。好的科幻小说都喜欢造概念,比如三体中的智子,如果没看过小说肯定不知道这是个啥玩意儿。软件系统在造概念这一点上,相比科幻小说只有过之而无不及,毕竟小说里的世界通常还是以现实为背景,而软件中的世界就全凭造物者(程序员)的想象(建模)了。稍微复杂一点的软件系统,都会引入一些领域特定甚至全新创作的概念。为了避免在项目过程中出现鸡同鸭讲的沟通障碍和理解歧义,就必须对描述这些概念的术语进行统一。而架构的一个重要目的,就是定义和解释清楚系统中涉及的所有关键概念,并在整个架构设计和描述过程中使用标准和一致的术语,真正做到让大家的沟通都在一个频道上。
  • 言之有物:就跟讨论产品交互时需要对着原型图、讨论代码细节时需要直接看代码一样,架构是在讨论一些较高维技术问题时的必要实物(具体的实物化形式就是所谓架构描述)。否则,要么一堆人对着空气谈(纸上谈兵都说不上),要么每次沟通时都重新找块白板画一画(费时费力且容易遗落信息,显然不是长久之计)。
  • 知识沉淀 & 新人培训:架构应该被作为与代码同等重要的文档资产持续沉淀和维护,同时也是项目新人快速理解和上手系统的重要依据。不要让你的系统跟公司内某些祖传遗留系统一样 —— 只有代码遗留了下来,架构文档却没有;只能靠一些口口相传的残留设计记忆,苦苦维系着项目的生命延续。

3. 架构决定了产品质量

如何衡量一个软件产品的质量?上图是 ISO/IEC 25010 标准定义的软件产品质量模型,包括以下 8 个大类:

  • 功能适合性:功能完整度、功能正确性和功能恰当性;
  • 性能效率:时间表现(e.g. 响应时间)、资源利用和容量;
  • 兼容性:共存能力(e.g. 多版本组件共存)和互操作性;
  • 可用性:可学习性、可运维性、用户错误保护(e.g. 自动纠错)、UI 美观度、可访问性;
  • 可靠性:成熟度、可用性、容错性、可恢复性;
  • 安全性:机密性、完整性、不可伪造性、权威性和可审计;
  • 可维护性:模块度、可复用性、可分析性、可修改性、可测试性;
  • 可移植性:可适配性、可安装性、可替代性。

上述质量模型中列出的所有点,都是架构设计需要着重考虑的。其中除了功能适合性以外,其他所有点都属于非功能需求的范畴,这也是区分架构好坏的真正分水岭 —— 好的架构设计,不会停留在仅满足功能需求这一最基本的需求层次上(最坏的架构设计也同样能做到),更重要且更难以应对的是其他众多的非功能需求。

当然,鱼与熊掌不可兼得。架构与人生一样,也是一场权衡的游戏,弄不好就跟第八季的龙母一样的下场:既要又要还要,最后反而什么都得不到。好的架构师更应该像雪诺同志学习,表面上“know nothing”,实际上“know everthing”:清楚系统所有利益相关者(stakeholders),努力挖掘各方的主要述求(concerns),相应平衡自己的架构决策(decisions),最终实现你好我好大家好的终极架构目标。

4. 我还能说出更多理由

要不是篇幅所限,这一页 PPT 显然不够装:

  • 架构包含系统所有最重要的早期决策,这些决策会进而影响后续所有大大小小的技术决策。因此,早期的架构设计需要非常严谨和慎重,要尽可能“一次做对”(虽然很难),否则越往后纠错的成本越高;
  • 架构在组织内具有非常高的复用价值,因为同一组织内的产品之间一定会具备很多共性(需求、限制、环境等),很适合在架构层面进行最大化复用,避免重复解决相似的问题;
  • 康威定律指出,软件架构反映了组织结构。这个结论反过来也成立:好的架构也会让组织结构变得更高效
  • 越庞大和复杂的系统,架构越重要,因为只有好的架构才能有效控制、管理和降低系统复杂度
  • 是不是越听越糊涂,仿佛架构有无数种诠释和意义?不必过于纠结,按照GoF的设计模式所述:Architecture is about the important stuff. Whatever that is. 对,管它是啥,记住架构很重要就够了。

如何设计一个好的架构?

理解了架构的概念和重要性后,真正的架构师修炼之路才刚刚开始。如何设计一个好的架构?这显然是一个非常博大精深的主题,但并不是本文的重点,因此这里只简单列举了一些基本思想(原则)和经典套路(模式)。当然,架构设计更接近一门经验学科,仅停留在能脱口而出一些玄乎而高大上的理论概念肯定是不够的,需要结合实际工作内容和业务场景多多实践和揣摩才行,否则只能算是徘徊在架构的门外,连入门都谈不。

1. 架构原则(principles)

SOLID 原则是一套比较经典且流行的架构原则(主要还是名字起得好):

  • 单一职责:与 Unix 哲学所倡导的“Do one thing and do it well”不谋而合;
  • 开闭原则:用新增(扩展)来取代修改(破坏现有封装),这与函数式的 immutable 思想也有异曲同工之妙;
  • 里式替换:父类能够出现的地方子类一定能够出现,这样它们之间才算是具备继承的“Is-A”关系;
  • 接口隔离:不要让一个类依赖另一个类中用不到的接口,简单说就是最小化组件之间的接口依赖和耦合;
  • 依赖反转:依赖抽象类与接口,而不是具体实现;让低层次模块依赖高层次模块的稳定抽象,实现解耦。

此外,我们做架构设计时也会尽量遵循如下一些原则(与上述 SOLID 原则在本质上也是相通的):

  • 正交性:架构同一层次拆分出的各组件之间,应该尽量保持正交,即彼此职责独立,边界清晰,没有重叠;
  • 高内聚:同一组件内部应该是高度内聚的(cohesive),像是一个不可分割的整体(否则就应该拆开);
  • 低耦合:不同组件之间应该尽量减少耦合(coupling),既降低相互的变化影响,也能增强组件可复用性;
  • 隔离变化:许多架构原则与模式的本质都是在隔离变化 —— 将预期可能变化的部分都隔离到一块,减少发生变化时受影响(需要修改代码、重新测试或产生故障隐患)的其他稳定部分。

2. 架构模式(patterns)

架构模式(architectural patterns)与我们常讨论的设计模式(design patterns)并不是一码事,但如果仅从“模式”这个角度去解读,两者的理念都是一致的:针对给定上下文中经常出现的问题的通用、可复用的解决方案。最主要的区别在于,架构模式会更高维抽象和偏全局整体(毕竟是运用在架构设计层面)。

常见的架构模式,既包括一些传统模式(e.g. 分层、C/S、MVC、事件驱动),也包括一些新兴玩法(e.g. 云原生、微服务、Serverless)。不同模式有不同的适用场景,没有哪一种模式能通杀所有需求。成熟的架构师应该像一个冷静到冒得感情的杀手,永远只会客观地评估和选择最适合当下的解决手段,即使那么做会显得简单乏味;相反,不成熟的架构师,一心总想着搞事情(e.g. 强行套用微服务架构),而不是真正搞定问题。

怎么描述你的架构设计?

有了良好的架构设计,万里长征之路就已经走了一大半。就像是青年导演第一次遇上好剧本,心潮澎湃两眼放光,仿佛已经预见了电影上映后的票房盛况。当然,剩下的一小半路,并不会如想象中那么平坦 —— 同样的剧本,不同导演拍出来会有质一样的区别。好的“最佳导演”,即使面对不是“最佳剧本”的剧本,也有能力拍出“最佳影片”。同样,好的架构师,也应该有能力描述好一个不错的架构设计;即使做不到为精彩的内容加分,也不应该因为形式上没描述好而丢分,否则就会像高考作文丢了卷面分一样憋屈和心酸。

1. 架构描述的意义

为什么要描述架构?让它只存在我深深的脑海里不行吗?西方人有句谚语:好记性不如烂笔头。任何没有持久化的东西都是易失的(volatile),就跟内存一样。另一方面,就如前文所述,架构是沟通协作的基础,不通过架构描述(Architecture Description)沉淀下来让所有项目干系人都能看到,那就失去了沟通和传播的唯一载体。

根据个人观察,大家对“架构需要描述”这一点都没异议,所以绝大部分项目都或多或少会产出一些有模有样的架构描述文档。但“有架构描述”和“有好的架构描述”,这之间的鸿沟是巨大的,甚至比“没有”和“有”之间的差别还大。如果你也跟我一样,饱经沧桑阅尽无数架构文档,曾拍手叫好心怀感激过,也曾拍着大腿愤怒不已过,应该也能感同身受。

2. 架构描述的方式

对于同一件事物,作家会选择用文字来叙述,而画家却会用图画。尽管两者想要传达的信息是一致的,但描述方式的不同也会带来效果上的巨大差异。架构描述也分文字(Text)图(Diagram)两种形式,两者各有千秋:

  • 文字的背后是由一套严谨和完备的语言作为支撑,因此其描述可以做到非常精准详尽,而且编写起来也很方便,随便打开个记事本软件都能写;此外,就跟写代码一样,文字很易于做版本管理,借助简单的文本 diff 工具就能一目了然地对比出不同版本之间的细节差异;
  • 相比而言,图并不具备以上文字所独有的特点,但也有自己的独特优势:图是直观形象的,顺应了人类与生俱来的视觉识别本能;图的表达能力更强,很多时候一小张图所能传达出的信息(比如空间位置关系、颜色分类、图标形状),也许用一千行字也不足以完整准确地描述出来,即所谓“一图胜千言”。

聪明的你冷笑了一声:哼,又不是小孩子非得做选择题,难道不可以文字与图都要吗?当然可以,理想的架构描述一定是图文并茂的。但现实世界显然比理想残酷,实际软件项目中很难给你留足时间先憋出一篇完美的架构文档。如果以成年人的思维去考虑投入产出比(ROI),那么你一定会优先选择画图。

3. 为什么你应该优先画图?

敏捷软件开发宣言中提到:相比详尽的文档,可运作的软件更加重要(Working software over comprehensive documentation)。这么说当然不代表就不用写文档了,只是提倡没必要写过于详尽的文档。为什么?因为详尽的文档需要耗费大量的编写和维护成本,不符合敏捷开发的小步迭代和快速响应变化等原则。

那么,在如今这个全面敏捷开发的时代,如何也顺应潮流更加敏捷地编写架构文档呢?ROI is your friend —— 不求多,但求精,尽量用最少的笔墨表达出最核心的内容。从内容上来说,ROI 高的部分一般是偏顶层的整体架构或最核心的关键链路,这点在后文的 C4 模型理念中也有体现。而从形式上来说,图在文字面前具有无与伦比的表达力优势,显然是 ROI 更高的选择。

4. 为什么你需要学习画图?

多画图是没错,但有必要专门学习吗?又不是素描彩笔水墨画,只是画一堆条条框框而已,稍微有点工程常识的都能上。画的有点丑?那没关系,顶多再动用点与生俱来的艺术美感,把这几条线对对齐那几个框摆摆正,再整点五彩斑斓的背景色啥的,不就显得很专业了嘛?

看到这里,屏幕前的你又轻蔑一笑:哼,显然没这么简单。确实,道理说出来大家都懂,架构制图与工程制图一样,都是一件需要下功夫认真严谨对待的事情。但现实中大部分人还真没这工夫去下那功夫,比如上面贴的两幅很常见的架构图。第一张图不用多说,这种草图自己涂涂抹抹挺好,但拿出来见人就是你的不对了。那第二张图呢,看上去似乎还挺像那么回事的?并不是,如果你更仔细地去揣摩,就能发现这张图底下所隐藏的很多模糊和不严谨之处(可参考这张图的来源文章:The Art of Crafting Architectural Diagrams)。

所以,能画图并不代表能画好图;要想制得一手既漂亮又可读的好图,还是需要经过持续学习与刻意练习的,很难仅凭直觉和悟性就能掌握其中的关键要领。此外,错误的图往往比没有图还要糟糕,即使你只是抱着“有图就行,差不多那个意思得了”的心态,也至少应该理解一些科学制图的关键要素,避免给本来就已经很复杂难做的项目又蒙上一层模糊滤镜,甚至起到混淆和误导的反作用。

5. 架构制图的目标

讨论具体的制图方法和工具前,我们需要先竖立清晰的制图目标。工具是人类进化的阶梯,但如果理解和利用不当,则很容易反过来被工具所限制甚至奴役,忘了最初发明和使用工具的初心。对于架构制图而言,已经有那么多形形色色的方法与工具,使用它们的初心是什么呢?我认为本质上都是想把制图这个过程从一门自由的手艺变成一项科学的工程:系统、严谨、完整、标准化,同时能做到可重复、可持续和高效。

P.S:当时做 PPT 太赶,所以从这个章节开始的配图,只能被迫走极简路线了,还请见谅。。。

架构制图方法与工具

经过前面几个章节的“简短”铺垫,相信大家对架构制图的背景知识都已经产生了足够的认知。本章节将会具体列举和描述一些典型的架构制图方法与工具,其中有常见的也有罕见的,重点是希望能通过各种方法的横向对比,加深大家对制图方法本质的理解。

1. 方法一:UML

UML 应该是大部分人最熟悉的制图方法了,最新的 UML 2.x 版本由以下两大类图组成:

  • 结构图(Structural Diagrams):通过对象、属性、操作和关系等方式,强调系统的静态结构,其中最常见的类型包括类图(Class Diagram)、组件图(Component Diagram)和部署图(Deployment Diagram);
  • 行为图(Behavioral Diagrams):通过展示对象之间的协作关系以及对象内部的状态改变,强调系统的动态行为,其中最常见的类型包括用例图(Use Case Diagram)、活动图(Activity Diagram)、时序图(Sequence Diagram)和状态机图(State Machine Diagram)。

作为通用的“统一建模语言”,UML 总共包含了 14 种不同类型的图,可以全面覆盖软件设计领域各种制图需求,当然也包括了架构制图。同时,也正是因为 UML 把自己当成了一门语言,因此其各种记号(notion)和语义(sematics)都有非常严谨的定义,不会出现模糊或者歧义问题。最后,UML 经过几十年的发展和推广,也早已成为世界范围内广泛使用标准规范,其所带来的的隐性价值就是:在团队内使用 UML 进行沟通的成本是比较低的,因为可以假定绝大部分技术人员都能理解UML的含义和用法。

然而,UML 也非万能(虽然历史上曾一度把它当成软件设计的银弹),它最被人诟病的缺点就是过于复杂。这也不能怪 UML,毕竟它就是要被设计为足够通用、严谨和强大的,这些目标都与“简单”背道而驰,并让它一步步演化到了今天这个复杂刻板的庞然大物模样。虽然上面我们自信地假定了技术人员大多都懂 UML,但这个“懂”如果带上一个程度量词,我觉得平均能到 20% 就不错了 —— 绝大部分也就能认识几个常见的类图、时序图,估计都很难准确说出类图中各种箭头的含义。

无论怎么说,UML依然应该是每个程序员的制图工具箱中最常用和必备的工具之一。当然,也不应该是唯一,因为下面也还有些不能错过的好东西。

2. 方法二:4+1 View Model

“4+1”是啥?不知道没关系,听过“6+1”吗?对,就是那个小时候常看的“非常6+1”节目。它跟“4+1”之间的关系,就跟它们与邵佳一、张嘉译和沈佳宜之间的关系一样,除了赶巧共用了同一个后缀发音以外,八竿子打不着。

所以,“4+1”到底是指什么?让我们来 Wiki 一下:“4+1”是一种视图模型(view model),可以通过多种共存的视图描述软件密集型系统的架构。这些视图基于不同项目干系人(利益相关者)的视点(viewpoint),例如:终端用户、开发者、系统工程师和项目经理。“4+1”由 4 种基础视图和一些经过挑选的用例或场景(即额外的“+1”视图)组成,各自的具体含义如下:

  • 逻辑视图(Logical view):描述系统为终端用户提供的功能,一般会通过UML中的类图和状态图来表示;
  • 过程视图(Process view):描述系统的动态行为,包括流程和交互等,一般会通过 UML 中的时序图、活动图和通讯图来表示;
  • 开发视图(Development view):从程序员的视角来阐述系统,也被称为“实现视图”,一般会通过 UML 中的组件图和包图来表示;
  • 物理视图(Physical view):从系统工程师的角度来描述系统,包括系统组件的物理拓扑、各组件之间的物理连接,也被称为“部署视图”,一般会通过 UML 中的部署图来表示;
  • 场景(Scenarios):通过一小组用例或场景来描述架构,包括系统中各种对象和进程之间的交互时序,也被称为“用例视图”。这些场景会被用于识别架构元素(architectural elements)以及阐述和验证整个架构设计,也可以被作为架构原型的测试起点。

虽然上面提到“4+1”的各种视图一般都是用UML图来表示,但实际上“4+1”本身是一种通用的视图模型,并没有限制绘图的记号和工具。对于工程师而言,这种偏学院派的方法可能这辈子都不会直接用到,但其中蕴含的一个关键架构制图思想非常有价值:架构需要通过多种视图来描述,而这些视图是来源于不同项目干系人的视点(角度);只有这样才能产生一整套全面、立体且客观的架构描述。

3. 方法三:C4 Model

C4 模型是一种“抽象优先”(abstraction-first)的架构制图方法,它也是受前面的 UML 和“4+1”视图模型所启发,但相对而言要更加简单和轻量,只包含少量的一组抽象和图表,很易于学习和使用。

1)定义、理念与关键思想

C4 模型通过容器、组件、代码以及人这几个抽象来描述一个软件系统的静态结构,它的核心理念是希望像 Google Map 一样,通过不同层次的细节,为代码建立一种可以放大缩小的导览图。它最关键的思想就是自顶向下对系统的静态结构进行逐级拆分,依次描述各层次对象的职责、关系和外部依赖。除了核心的层次化静态结构视图,它还可以包含动态视图、部署视图等补充视图。

上面的左图展示了 C4 模型中各层次抽象之间的映射关系:1 个软件系统由 1~N 个容器组成,1 个容器由 1~N 个组件组成,1 个组件由 1~N 个代码结构组成。右图是以简单的 Spring PetClinic 项目为例,演示了一个真实软件系统在 C4 模型下的层次结构:最上层就是 PetClinic 软件系统,它可以拆分为数据库、Web 应用等几个容器;Web 应用又可以进一步拆分出 ClinicService 这个组件,而这个组件下又包含了 ClinicService 接口类、ClinicServiceImple 实现类、Owner / Pet / Visit 等领域对象类。

使用 C4 模型进行架构制图,本质上就是对上述几种抽象进行可视化。具体的做法是依次建立如下几类从粗到细的结构图:Context、Container、Component 和 Code(可选),这也是 C4 模型名称的来历。

2)Level 1:System Context diagram

系统上下文图作为第一级(L1),提供了一个展示系统全貌的顶层大图(big picture)视角,包括最中心的软件系统、周边的用户以及其他有交互的系统。其中最关键的两个概念分别是:

  • (Person):即使用软件系统的用户,例如一个在线商城系统的消费者、运营小二、系统管理员等;
  • 软件系统(Software System):作为最高层次抽象,描述了给用户创造价值的软件制品;既包括当前正在设计的软件系统,也包括该系统所依赖(或被依赖)的其他软件系统。一个软件系统通常是由单个软件开发团队所负责。

在绘制系统上下文图时,不需要关心诸如技术栈、协议等任何底层细节。这类图的受众是最广的,因为任何人都可以理解并从中获取到足够的信息,包括技术人员和非技术人员,也包括团队内成员和团队外成员。

3)Level 2:Container diagram

通过 L1 的上下文图理解了系统在整个 IT 环境中的定位后,下一步就是把系统这个框框放大,详细看下其中包含了哪些“容器”(Container,注意不要跟 Docker 容器搞混了噢!)。C4 模型中的容器是指单个应用或数据存储,通常可以独立部署和运行(有独立的进程空间,通过 IPC 机制互相通讯),例如:SpringBoot 微服务、React SPA、移动 App、数据库、Serverlss 函数、Shell 脚本。

L2 的容器图不仅展示了系统的进一步职责拆分,还包括了主要的技术选型、容器之间的通讯方式等关键架构信息。这类图可以面向全部的技术人员,既包括架构师、开发者,也包括运维人员、技术支持等。

4)Level 3:Component diagram

继续前面的套路,下一步就是把系统中各个容器再分别进行局部放大,将每个容器进一步拆分成多个组件(Component)。在 C4 模型中,组件是指一组通过良好接口定义封装在一起的相关功能(通常运行在同一个进程空间内),例如:Spring 里的一个Controller(不只包括定义了 REST 接口的 Controller 主类,也包括背后所有相关联的实现类,如 Service/Repository 等)。

与容器图类似,L3 的组件图也不只包含了容器的组件划分,还包括各个组件的职责定义、技术与实现细节等。随着层次的下沉和细节的增多,组件图的受众范围进一步缩窄,一般只适用于软件架构师和开发者(其他角色没必要理解,一般也理解不了)。

5)Level 4:Code(可选)

再继续对组件进行放大,所能看到的最底层和细节的信息,就是 L4 的代码(Code)了。当然,这里所谓的“代码”还是以图的形式(e.g. UML 类图、数据库 E/R 图)展示类或文件粒度的代码结构,并不是真正的代码本身。即便如此,代码图在 99% 的架构描述场景下也依然过于详尽,一方面数量庞大,绘制成本很高;另一方面易于变化,维护成本也非常高。因此,一般只有非常重要和复杂的组件才需要用到这一层级进行描述。如果确实需要绘制,也应该优先考虑自动化的方式,比如很多 IDE 就支持自动生成 UML 类图。

6)补充图:Landscape / Dynamic / Deployment Diagram

除了上述各个层次的静态结构图,C4 模型还提出了一系列的补充图(Supplementary diagrams),包括:

  • 系统全景图(System Landscape diagram):全景图与系统上下文图的绘制方法类似,区别在于它是从企业或组织角度全景地展示出所有软件系统(包括与当前系统没有直接关联的)以及相关的用户和系统交互,即进一步放大架构图的 scope
  • 动态图(Dynamic diagram):由于结构图天生只能描述出系统的静态结构属性,因此 C4 模型中推荐使用 UML 中的通讯图、时序图等,对系统中关键链路的动态行为进行补充描述,即“动静结合”;
  • 部署图(Deployment diagram):除了缺失动态属性,上述结构图还有一个局限性:只描述了系统的抽象逻辑架构,并没有描述出系统实际部署时的具体物理架构。因此,C4 模型推荐再使用 UML 的部署图,对系统逻辑节点(一般是 L2 的“容器”粒度)与物理节点(e.g. 物理机 / 虚拟机 / Docker 容器 / 应用 Runtime)之间的映射关系进行补充描述,即“虚实结合”。

结合了这些补充图后的 C4 模型,才是可以全面与立体地描述出软件架构方方面面的完全体架构制图方法。

4. 方法四:arc42

严格来说,arc42 并不是一种架构制图方法,而是一个架构文档模板。虽然如前文所说,在架构描述中“图”是比“文字”更高优的选择,但实际项目过程中你终究还是需要产出一份相对完整、有图有文字的架构文档。arc42 就是专门用于帮助大家更好地编写架构文档;而作为架构文档中最重要的架构图,显然 arc42 也不会放过 —— 其中多个核心章节都与架构图有关,且详细描述了相应的制图方法。这里不会详细展开介绍 arc42(不能抢了下一篇文章的饭碗),只会简单介绍下 arc42 中制图方法与 C4 模型的异同。

伟大的思想都是相似的,arc42 也不例外。上方左图的右侧部分,概括了 arc42 模板中与制图相关的几个核心章节,分别是:

  • 第 3 章 - Context:该章节用于介绍系统的背景和上下文,因此其制图思路几乎等同于 C4 模型中的 L1(系统上下文图);
  • 第 5 章 - Building block view:该章节用于介绍系统的基本构成要素,按照官方指导思想也与 C4 模型中的自顶向下层次化拆分思想无异,唯一区是 arc42 并没有规定拆分的具体层次,只要有需要可以按照“黑盒 -> 白盒”的套路一直拆到底;
  • 第 6 章 - Runtime view:看名字就无需解释了,就等同于 C4 模型中补充的运行时视图;
  • 第 7 章 - Deployment view:同样地,这里也等同于 C4 模型中补充的部署视图;但有一点,arc42 强调部署视图也可以类似结构视图一样做自顶向下的层次化拆分(对于较为复杂的部署架构,层次化确实很有必要)。

因此,本质上 arc42 中提倡的制图方法与C4模型是等价和兼容的,完全可以配合使用:以 arc42 作为架构文档框架,其中的架构制图采用更具体的 C4 模型。这也是目前我们项目中实际采用的方法。

5. 其他方法 & 制图工具

除了上述几种方法以外,在软件行业蓬勃发展的数十年间也涌现出过很多其他的优秀架构制图方法,其中既包括一些通用方法,如:SysML、AADL、ArchiMate,也包括一些领域特定方法,比如在企业中后台业务建模场景中很常见的 BPMN。再详细地展开描述各个方法,显然会让本文又臭又长(虽然写到这里时似乎就已经注定了),有兴趣的读者可以自行检索和探索。

到这里为止,本章节介绍的都是架构制图的各种方法;而实际从方法到落地的过程中,还有一个绕不开的环节:选用什么样的工具去制图?总不能真的跟写工程制图作业一样用纸和笔吧?作为数字化改革的推动者,程序员们当然要全面拥抱数字化工具;大家日常工作中必然也已经积累了很多顺手的画图工具,因此这里我只推荐两个自己用得比较多的:

  • draw.io:这是一个开源的在线绘图软件,相信很多人都有用过。考虑到数据安全问题,推荐大家用完全离线的桌面版。作为一个程序员友好的绘图工具,draw.io 的最大优点就是支持三方插件,比如这个开源的 c4-draw.io 插件,可以帮助你更方便地在 draw.io 中绘制 C4 模型架构图;
  • PlantUML:作为文本制图的代表性工具,PlantUML 可以用于绘制各种类型的UML图,以及其他一些适用于文本制图场景的图(比如这个开源的 C4-PlantUML 扩展)。在这些场景下,文本制图具有可视化制图所无法比拟的优势:轻量、高效、版本化、自动化、一致性、易于复用等。虽然文本制图工具诞生已久(比如应用广泛的 [Graphviz](),最早发行于 1991 年),但相信随着现代各种 XXX as Code 的意识觉醒,这类 Diagram as Code 工具也会获得更多青睐(btw,语雀文档早已支持内嵌 PlantUML 制图)。

架构制图方法论总结

古有云:授人以鱼,不如授人以渔。推而广之:授人以方法,也不如授人以方法论。什么是方法论?虽然这个词在公司里已经用烂了,但确实有它的价值和意义:方法论(methodology)是对方法的更高维度抽象,由它可以推导出解决问题的具体方法(method)。理解了方法论,才能融会贯通,掌握解决问题的本质要点;你也不会再受限于单一的具体方法,因为使用任何方法都能快速上手和灵活运用,并得到差不多的同等效果。

因此,本文最后这一章节将对各种架构制图方法进行归纳总结,并尝试提炼出一个通用的架构制图方法论,期望能帮助大家更好地理解架构制图背后的原理和思想。即便现在所熟知的各种方法与工具终会过时,也依然能风轻云淡地看待它们的新老交替:过去是 UML,现在是 C4,未来是什么呢?这并不关键,因为即使方法过时了,背后的方法论也不会过时。

所以,那些茫茫多的方法背后,究竟是什么样的核心方法论在支撑着呢?经过作者呕心沥血冥思苦想了近 15 秒钟,终于总结出了如下这套经典方法论(p.s:就是凑数的,不要太当真~ )。由于其中包含了 5 个环环相扣的要点,我们姑且称它为:五环理论

1. 理解制图目标

架构制图的第一要点,是需要先深刻理解制图目标。正所谓“以始为终”,有了目标我们才能清晰地前行;否则漫无目的地乱窜,往往会多走不少弯路,甚至南辕北辙。架构制图的目标是什么?其实前文已经提到过很多,这里再简单总结下:

  • 准确(accurate):错的图比没有图还糟糕;即使一开始是准确的,后面也需要定期更新校对;
  • 完整(complete):需要覆盖架构的核心要素和关键信息,为受众呈现一个没有残缺的完整架构设计;
  • 清晰(clear):制图时最好带上图例(形状、颜色、线型、箭头);用图描述不清的地方,还可以加上文字标注做补充;
  • 一致(consistent):比如同一类型的图,最好使用相同的记号风格,以降低受众的理解成本;不一致往往还会带来混淆;
  • 简洁(consise):在满足以上 4 点基础之上,还需要让图更加简洁,一方面是更容易被人接受(没人读 = 没写),另一方面更新维护成本也更低。

2. 找准受众和关注点

架构制图的第二要点,是要找准你制图的受众(audience)以及他们各自的关注点(concern)。找不准的话,要么效果大打折扣(不是他们想听的),要么犹如对牛弹琴(他们根本就听不懂)。常见的一些受众和关注点可包括:

  • 研发:一般会关注很多实现相关细节,比如技术选型、实现可行性、可维护性等,毕竟他们是架构的最直接消费者;
  • 运维:不太关心应用内的具体技术实现(当成黑盒),但很关心各个应用实例的物理部署方式、网络连通性、可运维性等;
  • 安全:只关注系统是否有安全风险,例如是否可能被注入恶意代码、是否有权限漏洞等;如果经历过安全评审,应该很有体感;
  • 产品:大部分情况下只关心项目能否按期上线,其他方面...可能表面上表示些许关心,实际上要么并不在乎要么真的不懂。

3. 自顶向下逐层描述

架构制图的第三要点,是合理运用层次化(hierarchical)的套路,自顶向下逐层描述。无论是 C4 模型还是 arc42 模板,背后都深刻运用并显著强调了这一点。为什么一定要这么做?其中蕴含了两个普适的原理:

  • 分而治之:软件领域中,分而治之是控制和应对复杂系统的最有效方法。而层次化拆分,本质上就是一种分而治之手段:将系统按照从粗到细的粒度,一级一级地拆分成多个相对独立和低耦合的元素(子系统、应用、组件等);
  • 金字塔原理:这本书的核心观点就是,按照自顶向下的方式,先抛出主观点再依次用各个子观点去论证。这样的沟通方式更符合人类的思维逻辑,也更容易让读者接受。简单来说,就是要“先说重点”,帮助读者做归纳总结和划重点,而不是先抛出一大堆细枝末节的零散东西让读者自己去消化和推演。

4. 使用多种架构视图

架构制图的第四要点,是在向传统的工程制图方法论致敬:使用多种架构视图来描述你的架构。在工程制图的世界里,任何立体的制品,大到机床小到零件,都至少需要通过三种视图(主视图、俯视图、左视图)来描述。作为现实世界的映射,软件系统也是多维和立体的,只用单一视图不可能覆盖所有关键的架构信息;即使强行把这些信息都塞在一张图里,那也一定会复杂到让人无法理解。

在架构设计领域,架构视图(architectural view)有专门的定义:针对系统架构某一个方面(aspect)的一种描述;每个视图都会覆盖项目干系人的一种或多种关注点。从上述定义可以看出来,不同的架构视图会有不同的侧重点,同时在描述自己所专注的方面时也会略去与当前视图无关的其他细节 —— 这其实也是一种与层次化拆分类似的分而治之思想,只不过这里是针对完整系统的维度分解,而层次化则是针对某一具体视图再做自顶向下的垂直下钻(drill-down);两者是正交且可以相互配合的,例如前面说到的结构视图、部署视图甚至动态视图,都可以分别再进行层次化拆分。

5. 遵循规范和最佳实践

架构制图的第五要点,其实只是一句正确的废话:遵循规范和最佳实践。这一点已经不限于架构制图,而是上升到了工程实践领域的通用方法论层面。正如前面章节所说,“学习架构制图的目标,就是要把它从一门手艺变成一项工程”,因此架构制图的“施工”过程也理所应当符合工程化思维

  • 一方面,制图需要遵循明确的规范,在理论层面进行约束和指引,确保过程和产物的高质量与标准化;
  • 另一方面,制图还需要遵循业界最佳实践,在实践层面持续吸取优秀经验,不断精进自己和团队的制图技能。

附:架构描述标准化概念模型

国际上对架构描述其实建立了专门的标准(ISO / IEC / IEEE 42010:2011),其中的很多概念词汇在本文中都有提到(e.g. Stakeholder、Concern、View、Viewpoint),有兴趣的同学可以进一步研究下。

结语

如果你从头到尾耐着性子看到了这里,那么不用怀疑,你一定就是我们团队要找的那种能成大事儿的人:

欢迎各位技术同路人加入阿里云云原生应用研发平台 EMAS 团队,我们专注于广泛的云原生技术(Backend as a Service、Serverless、DevOps、低代码平台等),致力于为企业、开发者提供一站式的应用研发管理服务,内推直达邮箱:pengqun.pq # alibaba-inc.com,有信必回。

 

posted @ 2022-07-20 20:06  CharyGao  阅读(1605)  评论(0编辑  收藏  举报