一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构。任何一个软件模块都应该只对某一类行为者负责。
单一职责原则主要讨论的是函数和类之间的关系,但是它在两个讨论层面上会以不同的形式出现,在组件层面,称为共同闭包原则,在软件架构层面,则是用于奠定架构边界的变更轴心。
一个好的软件架构师会努力将旧代码的修改需求量降至最小,甚至为0。如何实现这一点?先将满足不同需求的代码分组(SRP),然后再来调整这些分级之间的依赖关系(DIP)
OCP是进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响。
如果想用可替换的组件来构建软件系统,那么这些组件就必须遵守同一个约定,以便让这些组件可以相互替换。
LSP可以且应该被应用于软件架构层面,因为一旦违背了可替换性,该系统架构就不是不为些增添大量复杂的应对机制。
应该在设计中避免不必要的依赖
任何层次的软件设计 如果依赖了它并不需要的东西,就会带来意料之外的麻烦。
该设计 原则指出高层策略性的代码不应该依赖于实现底层细节的代码,恰恰相反,这些实现底层细节的代码应该依赖高层策略性的代码。
如果想要在软件架构设计上追求稳定,就必须多使用稳定的抽象接口,少依赖多变的具体实现。
DIP通常是显而易见的组织原则。
REP原则就是指组件中的类与模块必须是彼此紧密相关的。一个组件不能由一组毫无关联的类和模块组成,它们之间应该有一个共同的主题或者大方向。
应该将那些会同时修改,并且为相同目的而修改的类放到同一个组件中,而将不会同时修改,并且不会为了相同目的而修改的那些类放到不同的组件中。
不要强迫一个组件的用户依赖他们不需要的东西。
是帮助我们决策类和模块归属于哪一个组件的原则。建议我们将经常共同复用的类和模块放在同一个组件中。
组件依赖关系图中不应该出现环
依赖关系必须要指向更稳定的方向
稳定性指标一种方法是计算所有入和出的依赖关系。
Fan-in:入向依赖,这个指标指代了组件外部类依赖于组件内部类的数量
Fan-out:出向依赖,这个指标指代了组件内部类依赖于组件外部类的数量。
I:不稳定性,I=Fan-out/(Fan-in + Fan-out),该指标的范围是[0,1],I=0意味着组件是最稳定的,I=1意味着组件是最不稳定的。
一个组件的抽象化程度应该与其稳定性保持一致
衡量抽象化程度,它的值是组件中抽象类与接口所占的比例
Nc:组件中类的数量
Na:组件中抽象类和接口的数量
A:抽象程度,A=Na/Nc
软件系统的架构质量是由它的构建者所决定的,软件架构这项工作的实质是规划如何将系统切分成组件,并安排好组件之间的排列关系,以及组件之间互相通信的方式。
设计软件架构目的是为了在工作中更好地对这些组件进行研发、部署、运行以及维护。
如果想设计一个便于推进各项工作的系统,其策略就是要在设计中尽可能长时间地保留尽可能多的可选项。
软件架构设计的主要目标是支撑软件系统的生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署。软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本 。
主要指的是组件拆分及如何组织
开发层面,将系统切分成一系列隔离良好、可独立开发的组件,然后才能将这些组件分配给不同的团队各自独立开发
部署层面,设计目标是快速部署。设计良好的架构不依赖于成堆的脚本与配置文件,也不需要 手动创建一堆目录或者文件。通过正确的划分、隔离系统组件来实现,其中包括开发一些主组件,让它们将整个系统黏合在一起,正确地启动、连接并监控每个组件 。
按层解耦根据变更原因分层。
按用例解耦主要指的是同一层下的再一次细分
主要是为了降低耦合
软件架构设计本身也是一门划分边界的艺术。边界的作用是将软分割成各种元素,以便约束边界两侧之间的依赖关系。
边界有些是在项目初期切分好,有些是在项目后期才划分。项目初始的边界划分目的是方便将一些决策延后进行,确保未来这些决策不会对系统的核心业务逻辑产生干扰。
架构师所追求的目标是最大限度地降低构建和维护一个系统所需的人力资源。系统中存在的耦合——尤其是那些过早做出的、不成熟的决策所导致的耦合是最消耗人力资源的。
过早且不成熟的决策指的是与系统的业务需求(用例)无关,包括采用的框架、数据库、web服务器、工具库、依赖注入等。在一个设计良好的系统架构 中,这些细节性的决策都应该是辅助性的,可以被推迟的,一个设计良好的系统架构不应该依赖于这些细节,而应该尽可能地推迟这些细节性的决策,并致力于将这种推迟所产生的影响降到最低。
边界线应该划在那些不相关的事件中间,比如GUI 与业务逻辑,数据库与GUI,数据库与业务逻辑
输入、输出即I/O是无关紧要的.
可以基于数据库和 GUI这两个为例来建立一种向系统添加其他组件的模式。
插件式架构:系统的核心业务逻辑必须和其他组件隔离,保持独立,而这些其他组件要么是可以去掉的,要么有多种实现。
为了在软件架构中画边界线,需要先将系统分割成组件,其中一部分是系统的核心业务逻辑组件,另一部分则是与核心业务逻辑无关但负责提供必要功能的插件。然后通过对源代码的修改,让这些非核心组件依赖于系统的核心业务逻辑组件。其实,这也是一种依赖反转原则(DIP)和稳定抽象原则 (SAP)的具体应用,依赖箭头应该由底层具体实现指向高层抽象的方向。
边界存在形式主要有单体结构形式、部署形式、服务形式
物理边界形式:动态链接库、本地进程 、服务
跨边界调用是由低层客户端来调用 高层 服务函数,这种依赖关系在运行时和编译时会保持指向一致,都是从低层组件指向高层组件。
本质上,所有的软件系统都是一组策略语句的集合。
整体业务策略通常都可以被拆解为多组更小的策略语句。
软件架构设计的工作重点之一就是,将这些策略彼此分离,然后将它们按照变更的方式进行重组分组。其中变更原因、时间和层次相同的策略应该被分到同一个组件中,反之,变更原因、时间和层次不同的策略则应该分属于不同的组件。
架构设计的工作通常需要装饰组件重排组合成为一个有向无环图。图中的每一个节点代表的是一个拥有相同层次策略的组件,每一条单向链接都代表了一种组件之间的依赖关系,它们将不同级别的组件链接起来。
在一个设计良好的架构中,依赖关系的方向通常取决于它们所关联的组件层次,一般来说,低层组件被设计为依赖高层组件。
业务逻辑是一个软件系统存在的意义,它们属于核心功能,是系统用来赚钱或者省钱的那部分代码,是整个系统中的皇冠明珠。
这些业务逻辑应该保持纯净,不要掺杂用户界面或者所使用的数据库相关的东西。在理想情况下,这部分代表业务逻辑的代码应该是整个系统的核心,其他低层概念的实现应该以插件形式接入系统中。业务逻辑应该是系统中最独立、复用性最高的代码。
架构设计的主题:软件的系统架构应该为该系统的用例提供支持。架构设计不是与框架相关的,不应该是基于框架来完成,框架只是一个可用的工具和手段,不是一个架构所规范的内容。
架构设计的核心目标:应该围绕着用例来展开,可以在脱离框架、工具以及使用环境的情况下完整地描述用例。良好的架构设计 应该尽可能地允许用记推迟和延后决定采用什么框架、数据库、Web服务以及其他与环境相关的工具。框架应该是一个可选项。良好的架构设计还应该让我们很容易改变这些决定。总之,良好的架构设计应该只关注用例,并能将它们与其他的周边因素隔离。
架构设计支持可测试。
六边形架构:也称为端口与适配器架构《Growing Object Oriented Software with Tests》
DCI架构
BCE架构:《Object Oriented Software Engineering: A Use-Case Driven Approach》
共同特点:
依赖关系规则:源码中的依赖关系必须只指向同心圆的内层,即由低层机制指向高层策略。
构建完整的架构边界就是为系统设计双向的多态边界接口,用于输入和输出的数据结构,以及所有相关的依赖关系管理,以便将系统分割成可独立编译与部署的组件。
构建不完全边界是在将系统分割成一系列可以独立编译、独立部署的组件之后,再把它们构建成一个组件。换句话说,在将系统中所有的接口、用于输入/输出的数据格式等每一件事都设置好之后,仍选择将它们统一编译和部署为一个组件。
单向边界:在设计一套完整的系统架构边界时,往往需要用反向接口来维护边界两侧组件的隔离性。使用依赖反转
门户模式:省去了依赖返转
负责创建、协调、监督其他组件的运转。
是系统中最细节化的部分,也就是底层的策略,是整个系统的初始点。在整个系统中,除了操作系统不会现有其他组件依赖于它了。
Main组件的任务是创建所有的工厂类、策略类以及其他的全局设施,并最终将系统的控制权转交给最高抽象层的代码来处理。
Main组件中的依赖关系通常应该由依赖注入框架来注入。
Main组件也可以被视为应用程序的一个插件,负责设置起始状态,配置信息,加载外部资源,最后将控制权转交给应用程序的其他高层组件。另外,由于Main组件能以插件形式存在于系统中,因此可以为一个系统设计多个Main组件,让它们各自对应于不同的配置。