架构整洁之道(五)-软件架构一
第十五章 什么是软件架构
软件架构的实质就是规划如何将系统切分成组件,并安排好组件之间的排列关系,以及组件之间互相通信的方式。
设计软件架构的目的,就是为了在工作中更好地对这些组件进行研发、部署、运行以及维护。
软件架构设计的主要目标是支撑软件系统的全生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署。
软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本。
一个优秀的软件架构师应该致力于最大化可选项数量。
优秀的架构师会小心地将软件的高层策略与其底层实现隔离开,让高层策略与实现细节脱钩,使其策略部分完全不需要关心底层细节,当然也不会对这些细节有任何形式的依赖。
优秀的架构师所涉及的策略应该允许系统尽可能地推迟与实现细节相关的决策,越晚做决策越好。
第十六章 独立性
按层解耦:一个系统可以被解耦成若干个水平分层,UI界面、应用独有的业务逻辑、领域普适的业务逻辑、数据库等。
用例的解耦:按用例将系统切分成多个垂直切分。
开发的独立性
只要系统按照其水平分层和用例进行了恰当的解耦,整个系统的架构就可以支持多团队开发,不管团队组织形式是分功能开发、分组件开发、分层开发都可以。
部署的独立性
可以在系统运行过程中热切换其各自分层实现和具体用例。
重复
有些重复是表面性的,如果有两段看起来重复的代码,他们走的是不同的演进路径,也就是说它们有着不同的变更速率和变更缘由,那么这两段代码就不是真正的重复。等我们几年后再回过头来看,可能就会发现这两段代码是非常不一样的了。
解耦模式
1.源码层次:控制源代码模块之间的依赖关系,实现一个模块的变更不会导致其他模块也需要变更或重新编译。也叫作单体结构。
2.部署层次:控制部署单元(如jar文件、DLL)之间的依赖关系,以此来实现一个模块的变更不会导致其他模块的重新构建和部署。
3.服务层次:组件间通过网络数据包来进行通信。
第十七章 划分边界
软件架构设计本身就是一门划分边界的艺术。架构师所追求的目标是最大限度地降低构建和维护一个系统所需的人力资源。一个系统最消耗人力资源的是什么?答案是系统中存在的耦合。
划分边界线
边界线应该画在那些不相关的事情中间。GUI与业务逻辑无关,所以两者之间应该有一条边界线。数据库与GUI无关,这两者之间也应该有一条边界线。数据库又与业务逻辑无关,所以两者之间也应该有一条边界线。
数据库与业务逻辑
类图
BusinessRules是通过DatabaseInterface来加载和保存数据的。而DatabaseAccess则负责实现该接口,以及和实际Database的交互。
组件图
Database组件知道BusinessRules组件的存在,而BusinessRules组件不知道Database组件的存在。
DatabaseInterface类是包含在BusinessRules组件中的,而DatabaseAccess类则被包含在Database组件中。
GUI与业务逻辑
插件式架构
软件开发技术发展的历史就是一个如何想方设法方便地增加插件,从而构建一个可扩展、可维护的系统架构的故事。
系统的核心业务逻辑必须和其他组件隔离,保持独立,而这些其他组件要么是可以去掉的,要么是有多种实现的。
由于用户界面在这个设计中是以插件形式存在的,所以我们可以用插拔的方式切换很多不同类型的用户界面。可以是基于Web模式的、基于客户端/服务器端模式的、基于SOA模式的、基于命令行模式的或者基于其他任何类型的用户界面技术的。
数据库也类似,将数据库作为插件来对待,它可以被替换成不同类型的SQL数据库、NoSQL数据库,甚至基于文件系统的数据库,以及未来任何一种我们认为有必要发展的数据库技术。
为了在软件架构中画边界线,我们需要先将系统分割成组件,其中一部分是系统的核心业务逻辑组件,而另一部分则是与核心业务逻辑无关但负责提供必要功能的插件。然后通过对源代码的修改,依赖反转,让这些非核心组件依赖于系统的核心业务逻辑组件。
第十八章 边界剖析
一个系统的架构是由一系列软件组件以及他们之间的边界共同定义的。而这些边界有着多种不同的存在形式。
1.低层客户端调用高层服务函数,这种依赖关系在运行时和编译时会保持指向一致,都是从低层组件指向高层组件。
从低层组件跨越边界到达高层组件的控制流
2.高层组件中的客户端需要调用低层组件中的服务时,需要运用动态形式的多态来反转依赖关系。系统在运行时的依赖关系与编译时的依赖关系是相反的。
反向跨越边界的控制流
第十九章 策略与层次
本质上,所有的软件系统都是一组描述如何将输入转化为输出的策略语句的集合。
软件架构设计的工作重点之一,就是将这些策略彼此分离,然后将它们按照变更的方式进行重新分组。其中变更原因、时间和层次相同的策略应该被分到同一个组件中,不同的策略分到不同的组件。
架构设计的工作常常需要将组件重排组合成为一个有向无环图。图中的每一个节点代表的是一个拥有相同层次策略的组件,每一条单向链接都代表了一种组件之间的依赖关系。这里提到的依赖关系时源码层次上的、编译期的依赖关系。在Java语言中指import语句。
一般来说,低层组件被设计为依赖于高层组件。
层次的定义?
层次是按照输入与输出之间的距离来定义的。一条策略距离系统的输入/输出越远,它所属的层次就越高,而直接管理输入/输出的策略在系统中从层次是最低的。
ConsoleReader和ConsoleWriter都属于具体类,由于他们与输入/输出最近,因此属于低层组件。
这个架构将高层的加密策略与低层的输入/输出策略解耦了。当输入/输出部分的策略发生变更时,它们不太可能会影响加密部分的策略。
低层组件应该成为高层组件的插件。
Encryption组件对IODevices组件的情况一无所知,而IODevices组件则依赖于Encryption组件。