架构整洁之道总结(the Clean Architecture)

总体感想

全书下来,印象最深、且最具有操作性的,就是:

依赖反转

源代码依赖方向永远是控制流方向的反转。 依赖关系必须要指向更稳定的方向:低层组件依赖高层组件,其中低层组件不稳定,高层组件稳定。 依赖关系应该指向更稳定的方向。

稳定抽象原则:一个组件的抽象程度应该与其稳定性保持一致。

系统的抽象程度和稳定性都可以量化,进而用来评测系统的质量。

系统在运行时的依赖关系与编译时的依赖关系是相反的:当高层组件的客户端需要调用低层组件中的服务时,我们就需要运用动态形式的多态来反转依赖关系。(第18章)

我们希望源码中的依赖关系与其数据流向脱钩,而与组件所在的层次挂钩。高层组件变更的原因具有“稀少而重大”的特点,低层组件变更的原因具有“频繁而不重大”的特点。(第19章)

章节小结

第2章 两个价值维度

系统行为,是紧急的。

系统架构,是重要的。

平衡系统架构的重要性与功能的紧急程度这件事,是软件研发人员自己的职责。因为业务部门本来就没有能力评估架构设计的重要程度。

请记住:如果忽视软件架构的价值,系统将会变得越来越难以维护,最终再也无法修改。如果系统变成了这个样子,那么说明软件开发团队没有和需求方做足够的抗争,没有完成自己应尽的职责。

第14章 组件耦合

组件依赖关系图不是系统功能单元关系图,而是应用程序在构建性和维护性方面的一张地图。所以组件依赖关系图不能像系统功能单元关系图一样在设计之初就确定下来,而是随着开发进程中出现的维护性和构建性的需求而产生的。

第15章 什么是软件架构

如果想设计一个便于推进各项工作的系统,其策略就是要在设计中尽可能长时间地保留尽可能多的选项。

如果在开发高层策略时有意识地让自己摆脱具体细节的纠缠,我们就可以将与具体实现相关的细节决策推迟或延后,因为越到项目的后期,我们就拥有越多的信息来做出合理的决策。 另外,我们保留这些可选项的时间越长,实验的机会越多,我们做决策的时候就能拥有越充分的信息。

一个优秀的软件架构师应该致力于最大化可选项数量。

第17章 划分边界

软件架构设计本身就是一门划分边界的艺术。

一个系统的GUI与业务逻辑的变更原因、变更速率显然是不同的,所以二者中间应该有一条边界。同样的,一个系统的业务逻辑与依赖注入框架之间的变更原因和变更速率也会不同,它们之间也应该画边界线。

这其实就是单一指责原则(SRP)的具体实现。SRP的作用就是告诉我们应该在哪里画边界线。

插件式架构

事实上,软件开发技术发展的历史就是一个如何想方设法方便地增加插件,从而构建一个可扩展、可维护的系统架构的故事。

第21章 尖叫的软件架构

架构设计的核心目标:

一个良好的架构设计应该围绕用例来展开,这样的架构设计可以在脱离框架、工具以及使用环境的情况下完整地描述用例。

这就好像一个住宅建筑设计的首要目的应该是满足住宅的使用需求,而不是确保一定要用砖来构建这个房子。架构师应该花费很多精力来确保该架构的设计在满足用例需要的情况下,尽可能地允许用户能自由地选择建筑材料(砖头、石料或者木材)。

而且,良好的架构设计应该尽可能地允许用户推迟和延后决定采用什么框架、数据库、Web服务以及其它与环境相关的工具。同时,良好的架构设计还应该让我们很容易改变这些决定。

总之,良好的架构设计应该只关注用例,并能将它们与其他的周边因素隔离。

第22章 整洁架构

整本书的核心总结。

clean architecture image

代码中的依赖关系必须指向同心圆的内层,即由低层机制指向高层机制。

比如下面这个例子就严格地遵守了依赖倒置原则,同时也就遵守了上面的设计原则:依赖关系指向同心圆的内层:

clean architecture example

第23章 展示器和谦卑对象

应用程序所能控制的,要在屏幕上显示的一切东西,都应该在视图模型中以字符串、布尔值或者枚举类型的形式存在。然后,视图部分除了加载视图模型所需要的值,不应该再做任何其他事情。因此,我们才能说视图是谦卑对象。

这里的视图有点像Redux模式下的View了:只负责忠实地将Model展现出来。

第24章 不完全边界

用于管理输入输出数据结构的双向多态边界接口,以及依赖反转关系的管理都需要耗费资源。某些情况下可以采用一些折中方案,采用某种不完全边界:单向边界、门户模式、省掉最后一步…。

第25章 层次与边界

“中庸”之道: 过度设计边界不对,一点不设计也是不合理的,我们能做的就是不断观察系统演进,权衡各个位置设计边界的成本与收益。

第28章 测试边界

测试: 针对系统组成部分来设计的测试是紧耦合的,比如针对每个类的每个方法进行的测试。最终这些测试都会被抛弃。

不要依赖于多变的东西。比如GUI一般是多变的,通过GUI来验证系统的测试一定是脆弱的。因此,我们在系统设计与测试设计时,应该让业务逻辑不通过GUI也能被测试。 怎么做:让GUI成为“谦卑对象”,只是忠实地反应一些数据(Redux模式)。

第34章 拾遗

这一章中,作者提到了一个重要的实现细节问题:访问权限修饰符(public、private等),以及该细节对整体设计架构的影响。

以网上书店为例,作者按照四种方法试图对系统进行模块化:按层封装,按功能封装,端口和适配器封装,按组件封装。每种模式都有各自的优缺点,但是这不是问题的重点。重点是:如果你在实现各个模式的时候,不能严格确定每个变量和方法的访问权限,那么所有的设计都等于“没有设计”。

四种设计模式图应该是这个样子:

model with access control attributes

其中虚化的部分代表只可在本包内可见的部分。

如果不能严格使用访问权限修饰符,那么理想中的模块化将会发生变化。极端一点,如果所有属性和方法都是public,那么这四种模式将变成下面的样子:

model with access control attributes

四种模式将完全一样!完全一样,只是由于我们没有严格地使用访问权限修饰符!

其它人的一些读后总结

为什么说我们需要软件架构图?

这篇文章中肯、实用,提出的一些观点具有很大的可操作性: 1. 我们常犯的最大的一个错误是为系统中具有高波动性的部分创建详细的架构图。除非是自动生成的,否则手动维护它们对我们来说就是一种负担。 2. 在实践中,大多数利益相关者对详细架构图不感兴趣,但会对一两个反映系统模块和边界的高级架构图感兴趣。除此之外,要深入理解系统,代码才是事实的来源。 3. 请停止为代码中自解释的内容创建详细的架构图,或者当没有真正受众时。 4. 直接询问利益相关者:你们希望从文档中获得什么信息?

推荐《架构整洁之道》

简单vs.简陋、平衡vs.妥协、迭代vs.半成品

这些拥有工匠精神的工程师们还是难以解决某些问题,这些人渐渐地发现,这个世界上有很多问题就像翘翘板一样,只能要一边,这一边上去了,另一边就下来了。就像要么用空间换时间,要么用时间换空间一样,你很难找到同时满足空间和时间要求的“双利解”;就像CAP的三选二的理论一样,这个世界不存在完美的解决方案,无论什么方案都有好的一面和不好的一面。而且,这些工程师还渐渐发现,每当引入一个新的技术来解决一个已有的问题时,这个新的技术就会带来更多的问题,问题就像有一个生命体一样,它们会不断地繁殖和进化。渐渐地,他们发现,问题的多少和系统的复杂度呈正比,而且不仅是线性正比,还可能呈级数正比,此时就越来越难做技术决定。但是有一些资深的工程师开始站出来挑战这些问题,有的基于业务分析给出平衡的方案,有的开始尝试设计更高级的技术,有的开始设计更灵活的系统,有的则开始简化和轻量化整个系统……这些高智商、经验足、不怕难的工程师们引领着整个行业前行。他们就是架构师!