软件架构的设计原则
设计原则就是架构设计的指导思想,它指导我们如何将数据和函数组织成类,如何将类链接起来成为组件和程序。反向来说,架构的主要工作就是将软件拆解为组件,设计原则指导我们如何拆解、拆解的粒度、组件间依赖的方向、组件解耦的方式等。这些设计原则往往会从以下几个维度考虑:
- 简单性:体现在应用架构是否有清晰、明确的层次划分,各应用系统之间的连接关系是否简单明确,系统之间的耦合程度低。
- 灵活性:体现在应用架构适应业务的快速变化,不仅要求在快速增加新应用时保持现有应用架构的稳定性,还要在适应业务变化的同时主动促进业务变革。
- 整合性:通过应用系统之间的解耦和组合,以统一的方式对外提供一致的服务接口,从而实现应用系统之间的共享和协作。
具体到编程的领域中,我们进行架构设计的主导原则是 OCP(开闭原则),在类和代码的层级上有:SRP(单一职责原则)、LSP(里氏替换原则)、ISP(接口隔离原则)、DIP(依赖反转原则);在组件的层级上有:REP(复用、发布等同原则)、CCP(共同闭包原则)、CRP(共同复用原则),处理组件依赖问题的三原则:无依赖环原则、稳定依赖原则、稳定抽象原则。不确定性原则要求抽象与隔离原则,抽象与隔离原则推导复用与依赖原则,复用与依赖原则延伸单源一致性原则与可测试性原则,单源一致性原则与可测试性原则保障了系统的相对确定性。
参阅:https://www.processon.com/view/5ff1b6e46376896cfa00ba88#map
架构设计总体原则
项目 |
描述 |
案例 |
领域视角原则 |
架构师必须要有领域工程意识,必须站在领域的角度,从多行业、多产品应用构成的领域维度来规划和设计架构。输出的系统架构设计,要能从“面”上回答解决了哪些问题,而不仅仅只是回答“点”上的问题。 |
比如,“购物车”是是否能做成全局统一的组件? |
系统视角原则 |
架构师必须要有系统工程意识,架构设计必须站在全系统的角度,对整个系统进行宏观整体把握,不能把软件/硬件等割裂开来,只考虑某一个方面。另一方面,系统设计中各个方面的能力应该是均衡的,没有明显的瓶颈和短木板。架构师在回答架构设计支撑了那些系统功能问题的同时,需要回答做了哪些平衡,为什么做这些平衡? |
比如 _ 在节省 10 ms 与 模型更加合理上的平衡与选择 _ 增加 5 ms 性能开销,但能增加系统后续的可维护性 |
重用原则 |
重用分为几个层次:架构重用、组件重用、设计重用、代码重用。领域架构设计强调的是领域内架构的重用和基于架构的组件重用。架构重用包括:逻辑架构重用和物理架构重用,在可能的情况下要尽量扩大重用的范围,特别是物理架构的重用,将带来巨大的价值。设计师在进行系统设计和实现时必须遵循重用原则,应用开发时,如果已有领域架构和平台,则其系统设计必须符合领域架构,并应用平台组件开发;如果无领域架构和平台,则需要考虑如何构建领域架构和平台为后续类似产品的重用和共享做好准备。 |
比如: _ TBBPM、QRExpression 就是组建级重用 _ 基于 HSF 的分布式服务治理架构,是属于架构重用 * 村淘二阶段物流需求参考运费险需求的设计实现,是代码级重用(最没价值的重用) |
商业目标原则 |
由于影响架构设计的各个质量属性之间存在一定的联系和冲突,比如:高的性能需求会导致成本的上升,高的可靠性要求也会导致成本的上升,扩展性的提高可能会牺牲一定的性能,而可移植性好则会提升架构的可重用性等等。因此架构设计时必须对各个质量属性的进行权衡,而权衡的依据就是架构设计的商业目标,包括:目标市场、架构的应用范围、上市时间、成本和收益、关键需求(质量属性需求)、开发过程和约束等,只有确定了商业目标才能确定架构设计的方向并对各个相互冲突的质量属性进行仲裁和权衡。 |
比如: _ 究竟是基于 HSF 构建统一的服务治理框架,还是基于 Dubbo? _ 增加实时的 HSF 服务监控,就没法做到 XXX 并发/台机器,并发性能有 10%的损耗等。 |
一致性原则 |
架构设计应该作为一个统一的整体而存在,架构设计在整体应该上是统一、和谐的,符合美学原则。架构中类似需求应该遵循相同的设计规范和标准,比如:接口、模型、术语等。架构中类似的处理机制应该是统一的:如异常处理流程。而可测试性、可调试性等设计等应采用统一的框架设计和实现。 |
比如: _ 一个 XML 解析,一个系统中用了 10 种框架,一个 JSON 解析,用了 4 个框架 _ “组件”的定义在各个人的理解都不一致,什么是“产品”?等等 * 有的产品用 JDK 8,有的用 JDK 7,有的用 6。有的产品用 JBOSS,有的用 Tomcat 等 |
够用/简单原则(JUST RIGHT) |
在达到预定的目标、具备所需要的功能前提下,架构设计应尽量简单,避免“过设计”,这样可减少处理费用,提高系统效益,便于实现和管理。简化组件/模块/方法/类的功能设计,避免设计面面俱到的多功能组件/模块/方法/类;调用功能时,避免功能过剩。架构设计应该在适当的范围内考虑其可扩展性,特别需要杜绝为了应用某种新技术而采用新的架构。 |
|
变化点分离原则 |
领域架构设计的核心就是通过设计支持各个变化点独立变化,因此在需求分析阶段就需要分离公共的部分和变化的部分,判断出哪些需求是相对稳定不变的,哪些需求是可能变动的,并将不同的变化点分离开;在逻辑架构设计时,工作重点也是使变化点在逻辑上隔离,并确定支持变化点的机制。根据那些稳定不变的需求设计体系结构,而根据那些可变的需求设计软件的“可扩展性”。分面和分层都是识别并隔离变化点的方法,使得架构具有良好的可扩展性。a、首先要确认软件中变和不变的因素,并根据变化频度进行分类。b、依据变化频度的分类进行层次划分,并标出各个层次的依赖关系。c、对不变的部分,设计得稳固、牢靠。对变化的部分,根据变化的频度设计其灵活性。 |
目前,buy-old 的设计相对而言,主要问题有缺少层次划分、缺少按功能域的进行分类。虽然有 Provider 扩展机制,但业务逻辑相互掺杂… |
逻辑与物理分离原则 |
系统功能逻辑与基于质量、成本等因素分割的物理实现并不存在直接的对应关系,同样的系统可能由于硬件技术和软件技术的需要,在功能部署和实现手段都会发生变化。架构设计强调多视角的设计,逻辑和物理分离就是在变化点分离原则的基础上,把逻辑功能和物理实现进行分离,在接口设计上就是把逻辑语义和接口形式以及物理承载分离开。 |
比如,在业务量比较少的时候,所有逻辑模块可以在一起部署。当用户搜索量上来时,搜索模块可以独立部署。。 |
支持分阶段交付原则 |
对于大型需求的设计,需要在架构设计时考虑要能够支持平台和产品分阶段/增量式实现和交付的要求,具有较强的可修改性和可扩展性。分阶段交付的版本之间要保持兼容性。 |
比如,现在有些业务初期很简单,需要快速上线,但后期越做越复杂。早起需求简单时,业务方自己实现了一套,在业务复杂后,想切回平台,发现平台也无法一下支撑。。根本原因:平台不支持简单业务在早期的快速上线,也不具备支撑业务从下发展到大的能力。 |
平面&层次&子系统划分原则
项目 |
描述 |
案例 |
高内聚/低耦合原则 |
子系统/模块的划分必须具有独立性,将联系比较密切、功能近似的部分集中,使得子系统/模块的内部功能、信息等各方面的凝聚性较好。子系统/模块之间的联系要尽量减少,接口设计简单、明确,尽量减少子系统/模块相互之间各种不必要的数据、调用和控制联系。一个内部联系强的子系统/模块对外部的联系必然是相对很少的。 |
比如,退款功能, 在 TP 中有,在 RP 中也有。TP 的退款功能会去修改 RP 的数据库表内容… |
数据冗余最小原则 |
架构设计应该使得系统中数据的冗余最小,否则可能引起相关的功能数据分布在各个不同的子系统/模块中,大量的原始数据需要调用和同步,大量的中间结果需要保存和传递,大量计算工作将要重复进行。数据冗余使得程序结构紊乱,不但给软件开发工作带来很大的困难,而且系统的工作效率和可靠性也大大降低了。 |
|
数据一致性原则 |
为了提高系统性能,备份节点和子系统/模块必要时需要对数据进行缓存,当发生变化时,必须有相应的机制保证缓存数据的一致性和有效性。 |
比如,Diamond 就是为了解决缓存同步、数据一致性问题。如果应用中有数据缓存,必须要考虑数据一致性问题。对于两个应用间的数据一致性也要注意是否有考虑? |
通用的平面划分原则 |
划分平面是隔离架构中变化点的常用方法。平面划分作为最高层次的系统划分,目的首先自然是进行功能的分类、分解,以及对系统复杂性分割、封装。平面划分的另一个目的是对系统设计所涉及知识领域的划分,或者说是工作的划分,便于让不同知识领域的专家专心于自己的领域开展下一步的设计工作。 |
比如,淘宝系统可初略划分成以下几个平面:买家平面、卖家平面、运营小二管理平面 不同的平面对于时延、可靠性要求、抖动性要求都不一样,需要区分对待。(比如买家数据分库分表的设计) |
通用的层次划分原则 |
划分层次也是隔离架构中变化点的常用方法。分层的目的是要实现可重用性、可更改性和可移植性。层次结构是一种应用非常广的架构模式。操作系统、数据库管理系统和网络通信软件都是层次结构软件系统的例子。对于追求屏蔽作用(屏蔽变化)的系统,首先需要把通用的部分和可变的部分分开。当通用部分和可变部分无法简单划分时,就需要一个抽象分析的过程。越是抽象的东西越不容易发生变化,越是具体的东西越是容易变化。 |
比如,TMF 2.1 层次划分为 行业定制、开放平台、交易场景、功能组件等 |
分层的单向依赖原则 |
采用分层模式时,原则上只允许较高层次依赖于较低层次,不允许出现反向依赖。 |
|
无循环依赖原则 |
架构设计中,子系统/模块之间的依赖关系要尽量减少,且不允许模块之间出现循环依赖。 |
一个不好的做法,为了解决循环依赖,将所有的模块都用一个工程来做管理,而不是考虑其他设计来解决依赖问题。 |
避免跨层通信原则 |
采用分层模式时,原则上不允许出现跨层通信,包括:API 调用、消息通信等。 |
比如,在展现层,直接调用 DO 对象的 API 方法进行操作。思考:BizOrderDO 对象,是否存在广泛的滥用? |
解耦原则 |
解耦原则要求架构设计中的各个模块之间是松耦合的,并且各个组件可以基于架构独立应用,这也是架构可裁减性的实现基础。 |
比如,我们采用的 Pandora,是有构建独立模块应用的想法。 |
实现无关性原则 |
构成架构的元素 DM 是与实现无关的,这也正是架构能够重用的关键。 |
简单的说:关键的 API,对外暴露的是接口,实现类不应暴露出去 |
灵活部署原则 |
架构设计要能够支持各模块灵活部署。构成架构的模块的粒度划分需要考虑不同应用场景的部署需求,同时各模块也是与实现无关的,其部署可以是灵活的,与物理位置无关。 |
|
接口设计原则
项目 |
描述 |
案例 |
接口标准化原则 |
架构设计中,模块之间的接口设计应该尽量采用业界标准协议和公司内部协议规范,以保持稳定性和兼容性。如果涉及对外的互操作等则建议采用用业界标准协议。 |
对于自行定制的接口,在接口发布后,最好要能发布配套的接口规范。并需要能保证后续接口对规范的前向兼容。 |
接口扩展性原则 |
接口设计要具有良好的可扩展性,比如通过预留字段等方式以方便今后的扩展。 |
但,不能将预留字段作为万能字段,任其自身自灭,不断腐化。比如,BizOrderDO 的 Attribute |
接口兼容性原则 |
系列化接口中所有版本应该保持兼容性,其基础通用功能和关键特性接口(如软件升级)设计在一个系列版本之内必须保持兼容,如:1.x 系列、2.x 系列 |
在之前做过的需求中,就发现。部分 API 升级后,就要求集团所有用到此 API 的都需要升级,因为接口参数发生了变化,输入/输出产生了变化。 |
接口抽象性原则 |
接口设计要具有抽象性,接口定义应该强调接口的逻辑语义而不是实现方法,以屏蔽掉具体实现的差异。 |
比如,“增加用户” 接口定义就是屏蔽了实现,而 “增加 LDAP 的用户”就是暴露了实现方式。 |
质量属性设计原则
项目 |
描述 |
案例 |
可重用性原则 |
架构或基于此架构的某些组件能够在未来的产品应用开发中能够重复使用。这要求架构设计时要充分分析其应用场景、范围和实现技术,以及不同的质量属性。 |
与“架构总体设计”原则类似。 |
可扩展性原则 |
可扩展性是通过变更系统增加新功能的能力和容易程度,架构设计根据其规模和复杂性对可扩展性有不同的要求。稳定性和可扩展性之间存在辩证的关系:如果架构不可扩展的话,那么就没有发展前途,所以不能只关心稳定性而忽视可扩展性;“可扩展”的前提条件是“保持架构稳定”,否则相同难以按计划开发出来,稳定性是使系统能够持续发展的基础。 |
|
可修改性原则 |
基于该架构产品及平台的可修改性(Modifiability) ,是与系统架构关系最为密切的一个质量属性。1、基于领域架构的产品能够进行快速修改,修改代价低,架构不限制组件设计及局部设计的修改。2、对外接口稳定,对内接口:机制稳定、可演进、可扩充。3、领域架构本身具有自我扩展性。分层是一种通用的可修改性实现方法,通过分层,可以使得修改被限制在各层之内,各层的修改不会影响其他层次的设计和实现。 |
|
可移植性原则 |
架构设计不依赖于特定的运行环境,基于该架构的产品及平台具有能够在不同软件或(和)硬件环境下运行的能力。如果对任何特定运行环境的所有假设都仅包含其中一个组件(或者在最坏情况下,包含在少数几个易于修改的组件)中,则该架构是可移植的。一般通过分层模式来提高架构的可移植性,在架构中对平台相关实现细节的封装表现为可移植性层。 |
比如,优惠功能域中的计算器,应该可设计为可移植的。 |
兼容性原则 |
架构设计上要支持不同应用场景以及系列化版本之间的兼容性设计。兼容性有两个方面的含义:一是版本的前向(forward)和后向(backward)兼容,另一个是对运行环境的兼容性。运行环境包括软件环境(如 OS)、硬件环境(如硬件平台)和网络环境(如接口兼容性)等,运行环境的兼容性要求与可移植性要求是相同的。后向兼容是兼容老版本,前向兼容是指将来可以被新版本所兼容,所以前向兼容本质上是特殊的可扩展性需求。 |
软件领域,比较常见的不兼容主要体现在,新上了某个业务之后,由于对一些接口的改动,导致之前的某个老业务的某些特性不可用了,功能丢失了。 |
可伸缩性原则 |
架构设计在不同的应用场景下具有一定的可伸缩性,包括容量、规格等指标。在线扩容能力就是一种可伸缩性,当用户增长时,可以通过增加服务器、虚拟机、Docker 容器等不同级别的手段来适应。 |
|
可裁剪性原则 |
可裁剪性是指用户从系统中删除部分功能的难易程度,或者从系统中选择部分功能丢弃其他功能的难易程度。可裁剪性有利于提高架构的可重用性。架构和平台设计必须具有良好的可裁剪性,可裁减性可以看成是可配置性的一个方面。可裁减性设计要求尽量降低组件之间的耦合和依赖关系。 |
|
性能原则 |
架构设计必须满足预定的性能目标,做好性能、成本、可扩展性和可移植性等的综合权衡,并能够根据应用场景的变化具有平滑扩容的能力。性能需求主要包括两个方面,一是吞吐量(Throughput),指系统或部件在指定时间段内执行的工作量,二是响应时间或速度,指事务或请求与响应之间的平均延迟。系统性能还包括:容量(Capacity);资源占用率(Resource Consumption),如内存资源的使用效率,CPU 资源占用率;Memory footprint,是指程序被加载到内存中所占用的静态内存大小。 |
|
可用性/可靠性原则 |
架构设计要满足预定的可靠性/可用性指标。不同的应用场景对可靠性指标的要求是不同的。系统的可靠性指系统硬件和软件在运行过程中抵抗异常情况的干扰及保证系统正常工作的能力。衡量系统可靠性的指标是平均故障间隔时间和平均维护时间。前者指平均的前后两次发生故障的时间,反映了系统安全运行时间,后者指故障后平均每次所用的修复时间,反映系统可维护性的好坏。 |
|
安全性原则 |
架构设计必须满足目标应用场景的安全需求。安全性是衡量系统在向合法用户提供服务的同时,阻止非授权用户使用的能力,以及防止意外或恶意访问限制性的资源或大量的资源,试图显示数据、改变/删除数据、访问系统服务、降低系统的可用性。 |
|
可测试性/可调试性原则 |
架构设计必须具有良好的可测试性和可调试性。良好的可测试性/可调试性能够方便系统缺陷的发现,降低对工具、仪器种类和专业测试设备和仪器的要求,并且在发现问题后能够快速检测和定位故障。设计中应该统一考虑可测试性/可调试性框架的设计和实现,尽量减少测试软件和产品软件之间的耦合性。 |
|
可生产性/可制造性原则 |
略 |
|
可安装性原则 |
可安装性是指完成设备安装的难易程度。针对用户安装使用场景,是否有针对性设计来提高系统可安装性。 |
|
可配置性原则 |
架构设计上提供,能够方便地支持组件的裁剪和配置。 |
比如,基于运营平台,进行业务配置。 |
成本原则 |
架构设计要从全局出发,不能只考虑单方面的成本,要综合考虑软件、硬件、整机、生产、备货、发货、测试等各个方面,以达到系统的最优成本。 |
互联网行业重点要考虑服务器、占地场租、电费等方面的成本。 |
易懂性原则 |
好的架构设计必然是易于理解,易于掌握的,同时符合自然原则和美学原理。良好的易懂性同时也有利于提高系统的可维护性。 |
|