架构整洁之道导读(二)续

关于组件聚合张力图的讨论

周三的午休时间,我在ThoughtWorks北京办公室分享了一场《架构整洁之道导读》。当谈到分享组件聚合原则的时候,很多同事表示难以理解。究其缘由,是我们无法将组件违反原则的后果对应到真实项目的问题上,这就导致原则和实践之间的不一致。讨论的过程异常激烈,但是很遗憾地最终并没有得到一个服众的结论。所以为了进一步澄清这些争议点,我决定专门组织一场针对组件聚合原则张力图的讨论会。在吴大师的鼓动下,时间定在下周四晚上的8点半,与会人员大多是咨询团队的技术教练,也有我们项目上的客户。

在这场长达两个半小时的讨论会上,没想到首先出现争议的点居然是组件的定义。

组件是软件部署的最小单元,是整个软件系统在部署过程中可以独立完成部署的最小实体。

对于这样的定义,大魔头提出了质疑:library(库)并不能独立部署。但凡出现明显的逻辑漏洞的时候,我们最好的方式是抛开译文回去看原文。

Components are the units of deployment. They are the smallest entities that can be deployed as part of a system.

阅读原文之后,我们发现“组件是软件部署的最小单元。”这句话翻译得并没有太大问题,但是第二句就有损原意了,原意是说可以作为系统的一部分被部署的最小实体,而没有强调部署过程这种动态的概念,否则就和前一句是同义反复。所以这个定义里面并没有说组件可以独立部署。后面提到组件可以被链接到一个独立可执行文件或者归档文件,又或者,可以被打包成.jar、.dll或者.exe文件,并以动态加载的插件形式实现独立部署

解读组件的定义

来自原文:

Components can be linked together into a single executable. Or they can be aggregated together into a single archive, such as a .war file. Or they can be independently deployed as separate dynamically loaded plugins, such as.jar or .dll or .exe files.

来自讨论:

20:56:56 From tianjie : These dynamically linked files, which can be plugged together at runtime, are the software components of our architectures.

联系上下文理解之后,我们知道:组件可以被设计成独立部署的,但是并不是所有的组件都是可以独立部署的。这是要澄清的,不然讨论聚合原则的时候容易出现偏差。

吴大师接着解释说,组件应该是个逻辑单元,而不是物理单元。强制某个代码模块就是一个物理的部署单元是不合适的。另外,鲍勃大叔在介绍架构边界时,也表明了一样的观点:架构的边界并不是服务的边界。

解读REP原则

我按照自己的思路解释过REP、CCP和CRP原则[1]之后,讨论的焦点很快聚集到REP原则的解读和实践意义上。

吴大师认为REP原则如果简单解读成没有发布过程就不能复用,它就和CCP、CRP原则的排斥力量不均衡,无法形成稳定的三角关系,那么这个张力图就显得有点鸡肋。

尚奇受到CAP(分布式系统基本原理,一致性,可用性和分区容错性)原则的启发提出了另一个解读方向。他说,CAP原则在分布式系统的实践里,都会先站住P原则,然后在C和A中权衡。那么在REP、CCP和CRP三角关系里,REP原则就相当于这里的P原则,必须先满足然后再去取舍CCP和CRP。

大魔头理解REP的意思是可复用性就是组件是独立可复用的。假如回到没有Maven这些工具,没有依赖管理的年代,如果我们所依赖的包还依赖其它第三方包,那么这个包就不能叫做独立可复用。

21:13:04 From YangYun : 我倒是理解REP的意思是你发布出来的一个可重用的包就是独立可重用的,你不能让我必须带着别的jar包才能用它。
21:14:04 From YangYun : The granule of reuse is the granule of release

他接着说,假如有两个提供同样功能的包,其中一个没有第三方的依赖,而另一个有,那我当然选择前者。

技术教练Sara举出了一个相对复杂但是很有启发性的例子。

21:46:35 From Qian Ping : 假设项目包含sub module ABC

  • 如果ABC单纯sub module没有打成jar,又互相直接复用了,就是违反了REP
  • 如果每个sub module,打成jar,互相复用的时候是通过对方特定版本的jar(如snapshot版本),就是符合REP
  • 如果符合REP了,而所有sub module是跟随整个项目一起升级版本,就是符合CCP因为他们是一体一起发布的
  • 这时假如A依赖B和C,我这次单纯想改C,他们一起升版本了。但其实B的Jar完全没有变化,这个对B来说就是一个不必要的发布,B又貌似应该分离出去,但如果它分离出去了,就又离REP和CCP远了

对于最后一句的表述,她澄清道:

之前有遇到一个情况,比如组件A,然后它里面需要用到一个common library, lib里面其实包含了比如3个sub module(1/2/3),全部都是A需要复用的, 这时候如果要改1/2/3里面任意的东西,都会一起升级lib,然后在A里面对应升级版本。

后来,有一些新组件B,它只需要用到common lib里面的3,不需要1/2,于是3一直被改和打包版本。 此时1/2会跟着升版本号,但其实1/2内容本身是完全没有变化的,只是版本号升了。

这个场景中引入了两个组件A和B分别依赖common library的某些模块。在我们讨论一个组件依赖时,面临的约束要简单很多,但是复用的初衷就是给多个组件去依赖,所以这个假设是很有价值。

Sara分析的思路如下:

如果分离出去,等于我有两个common lib(1/2 和 3), 对于B来说,B只需要3这么一个lib是比较完美的,反正改了3再改B就好了。

但对于A来说,它就需要同时升级1/2的lib和3的lib,等于要3个发布,而它原来只需要2个发布(1/2/3 + A),所以离CRP远了,同时它也要分别维护两个lib分别的版本升级,所以CCP也比原来差了。

在她的分析下,我们发现CRP和CCP不单是互相排斥的,还有可能两者都无法满足。造成这种结果的原因在于1/2/3模块形成的这个common library对于A组件而言都符合CCP和CRP原则,但是对于B组件而言,是不满足REP和CRP原则的,因为每次想要依赖3模块,就得全部依赖1/2/3整个common library(复用困难)。反之,如果我们将3从1/2/3中拆出来成为独立的组件,那就几乎宣告对于A组件而言势必违反CCP和CRP原则,但是B组件却获得了符合REP和CRP原则的好处。

她接着补充道:

其实后来说起对应微服务的时候有另外一个想法,就是比如说我系统里面多个组件需要用计提(Mark to market[2])这么一个功能,说白了就是一条公式,那通常可以有几个做法

  • 直接把这个公式复制到要用的组件,code level的复用,没有版本 -> REP bad, CCP bad, but CRP not bad (因为要更改时候发布次数还是一样的)
  • 把公式写到一个common lib里面再进行复用 -> REP good, CCP good, CRP bad(多发布一次)
  • 把公式放在一个独立service -> REP good, CCP bad(因为要维护多一个服务), CRP good

这个观点就上升到不同层次的复用性上,可以算是对组件聚合原则的普适性的探索。

当话题再次被聚焦到复用性时,技术教练MoMo提出一个观点:我们现在讨论就是可复用组件应该遵循的原则,而REP是对复用粒度的定义。至于那些那些常年采用SNAPSHOT(Java项目里Maven常用的开发版本号),没有发布概念的组件,就不该纳入复用的考虑范围内,那些也就不是REP的反模式。

与此同时,阎王指出了一个翻译上的失误。组件粘合张力图中REP原则的简短描述是“为复用性而组合”,而原文其实是"Group for reusers",翻译过来应该是为了复用者而组合,复用性的英文是 Reusability。所以为了复用者发布,考虑的就是对外部的承诺。

tension diagram

外部资料

大魔头在加班写方案和讨论的间隙,快速查阅了一些资料,比如wiki上对于REP原则的定义:

21:45:05 From YangYun : Reuse-release Equivalence Principle (REP)
REP essentially means that the package must be created with reusable classes – “Either all of the classes inside the package are reusable, or none of them are”. The classes must also be of the same family. Classes that are unrelated to the purpose of the package should not be included. A package constructed as a family of reusable classes tends to be most useful and reusable. - wiki百科里

在wiki的定义里,可以看到REP原则包含CRP和CCP原则的成分,如此看来,这三大原则并不符合MCME分类原则,就连鲍勃大叔在书中也是模棱两可的态度——REP维护共同的大主题,组件中的类和模块也必须紧密相关,这基本是CCP和CRP的简版描述。

然后大魔头查找到“粒度”这个词在软件设计中详细定义,这是对REP原则定义(软件复用的最小粒度等同于其发布的最小粒度)的分解和再认知。

21:57:03 From YangYun : http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/granularity.pdf

granularity

21:58:29 From YangYun : https://fi.ort.edu.uy/innovaportal/file/2032/1/design_principles.pdf

design principles

这些观点和学术建议很有代表性,值得大家反复揣摩和思考。

反模式

软件工程师一般有个“正难则反”的习惯。原则较抽象,但是模式很具体,反模式更能指导实践。接下来,大家开始讨论哪些是违反了REP原则的反模式。

首当其冲的就是git submodule,在某些项目中,这种通过源代码划分模块并共享的方式还是挺常见的。因为共享的是代码,所以每次共享代码更新,势必要让依赖方重新编译,发布和部署。这种做法对于复用是痛苦的。

其次是常年使用SNAPSHOT版本的某些项目。这些项目的特点一般都是某个产品团队底下,内部团队之间有复用的要求。缺点其实也很明显,常年SNAPSHOT等于没有版本和发布的流程。使用者并不知道SNAPSHOT中哪些是稳定的,哪些是修改的,拿到的版本到底是最新的还是遗留的,我需要的功能在这个功能有包含,还是你包含了太多我不需要的升级。这种也是复用痛苦的。

REP原则小结

综合以上两个例子以及其它讨论,我们得出了一个好玩的结论:软件工程发展到现在,REP原则已经是基本的要求,它的存在有可能是鲍勃大叔年代感老了的体现。

[1] 架构整洁之道导读(一)编程范式
[2] 架构整洁之道导读(三)组件耦合


于 2018-11-12


  1. 架构整洁之道导读(二)组件聚合

  2. Mark to market 按市值计价

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容