一线架构师实践指南(二)
Conceptual Architecture阶段
有经验的架构师不会一上来就关注如何定义“接口”,他们在大型系统架构设计的早期比较注重识别重大需求、特色需求、高风险需求,据此来设计概念架构。概念架构是对系统设计最初构想,就是把最关键的设计要素和交互机制确定下来,然后考虑具体技术的运用,设计出实际架构。概念架构应该抓大局、不拘小节。虽然概念架构都跳不出“架构=组件+交互”的基本定义,但它们描述架构的具体方式还是有比较大的差异:有点重视逻辑层、有点重视物理层、有的通过隐喻表明机制、有的看上去似乎就是一些设计元素的组合。不同的概念架构视图中,“链接”代表的含义千差万别:有的是依赖方向,有的是控制方向,有的是控制方向,有的是控制数据流向,因此,必须根据具体情况而定。在概念架构设计中,不关注明确的接口定义;之后才是“模块+接口”一级的设计。对大型系统而言,这一点恰恰是必需的。
概念架构界定西塘的高层组件,以及它们之间的关系。概念性架构意在对西塘进行适当分解,而不陷入细节。概念架构规定了每个组件的非正式规约及架构图,但不涉及接口细节。
概念架构满足“架构=组件+交互”的基本定义,只不过概念架构仅关注高层组件。概念架构对高层组件的“职责”进行了笼统的界定,并给出了高层组件之间的相互关系。概念架构不应涉及接口细节。需求不同,所以架构不同;当然,“需求”不是指“功能需求”,而是包含了功能、质量、约束等方面。进行概念呢架构设计时应确立架构大方向。架构设计贵在有针对性,概念架构设计针对重大问题、特色需求、高风险需求的要求,给出高层次的解决方案,这是概念架构的重要意义。所谓“被选架构方案”经常是概念架构一级的,有助于架构的对比分析、评审优化。
架构设计的驱动力 = 功能 + 质量 + 约束。用例技术是功能需求实际上的标准,用例技术涉及,但无法全面涵盖非功能需求。
初步设计的目标简单而明确:那就是发现职责。初步设计无需展开架构设计细节,否则就背上了“包袱”,这是复杂系统架构设计起步时的大忌。初步设计识别出了职责,后续的高层分割方案才有依据,因为每个“高层分割单元”都是职责的承载体,而分割的目的也恰恰在于规划高层职责模型。
实际的工程化实践中,需求捕获、需求分析、系统分析不是完全孤立进行的。相反,它们往往是相互伴随、交叉进行的。
有的书上明确地说“实体对象就是持久化对象”,这是错误的。因为实体对象涵盖更广泛,它可以是持久化对象,也可以是内存中的任何对象。实体对象不等于持久化对象,这个正确认识将有助于你的实践。
分析与综合是思维方向相反的过程。一般是先分析后综合,没有分析就不能有综合;没有综合的分析,也只是片面的分析。
“架构=模块+接口”的做法,其不足可概括为两点:
第一,忽视了多视图。“模块+接口”仅是逻辑架构设计视图的核心内容,而软件系统的架构设计还可能涉及开发视图、运行视图、物理视图、数据视图等多方面的考虑。
第二,忽略了概念架构设计。对规模稍大的系统而言,都必须先根据重大风险(包括功能方面、质量方面、约束方面),有针对性地定制包括“高层分割”在内的设计决策,然后才是“模块+接口”一级的设计。
架构设计中“高层分割”的两种实践套路:
l 切系统为系统;
l 切系统为子系统。
高层分割很重要,但不是概念架构的全部。除了切分决策之外,概念架构还包括技术选择、权衡策略等种类的决策。
Refined Architecture阶段
概念架构难以支持并行开发。要支持开发组相对独立地进行工作,须要提供指导和限制作用更明确的“规约”一级的设计。细化架构和概念架构之间存在如下典型差异:
l 接口。在细化架构中,接口占据非常核心的地位,而概念架构并不关心明确的接口定义(只有抽象的组件和抽象的交互机制)。
l 子系统。细化架构重视通过子系统和模块来分割整个系统,并且子系统往往有明确的接口;而概念架构中只有抽象的组件,这些组件没有接口,只有职责,一般是处理组件、数据组件或链接组件中的一种。
l 交互机制。细化架构中的交互机制应是“实在”的,如基于接口编程、消息机制或远程方法调用等;而概念架构的交互机制是“概念化”的。
当然,概念架构和细化架构都满足软件架构的定义——无论是“架构=组件+交互”,还是“架构=重要决策集”。
方案=“项目+需求+架构” 的总揽,方案!=架构的全部。
架构设计是一门解决复杂问题的实践艺术,于是,以分而治之为核心思想的多视图方法必不可少。Refined Architecture(细化架构)属于架构设计,不能与Detailed Design(详细设计)相混淆。
不同涉众看待软件架构的视角是不同的。多视图方法有两方面的实际意义:
l 利于思考(因为分而治之的思维方式);
l 便于交流(因为在一定程度上分离了涉众关注点) 。
5视图方法包括:逻辑视图、开发视图、运行视图、物理视图、数据视图。5视图各有其“思维立足点”,分别是:
l 职责划分(逻辑视图)
l 程序单元组织(开发视图)
l 控制流组织(运行视图)
l 物理节点安排(物理视图)
l 持久化设计(数据视图)
看似复杂的5视图方法其实很简单,因为其每个视图都是从特定角度规划系统的分割与交互,都是(架构的定义)“组件+交互”的一种体现。
一线架构师最缺的不是理论,也不是技术,而是位于理论和技术之间的“实践策略”和“实践套路”。
分层是最常用的架构模式。在架构设计初期,100%的系统都可以用分层架构,就算随之设计的深入而采用了其他架构模式也未必和分层架构矛盾。
为了得到客户经常性的反馈,快速迭代有个基本前提:开发应该“深度优先”,而不是“广度优先”。为了支持迭代开发,逻辑架构设计中必须引入分区。分区是一种单元,它位于某个层的内部,其粒度比层要小。一旦架构师针对每个层进行了分区设计,“深度优先”式的迭代开发就非常自然。
机制才是设计的灵魂所在,否则我们就将不得不面对一群无法相互协作的对象,它们相互推搡着做自己的事情而毫不关心其他对象。软件系统中的机制,是指预先定义好的、能够完成预期目标的、基于抽象角色的协作方式。机制不仅包含了协作关系,同时也包含了协作流程。对于编程实现而言,在没有提取机制的情况下,机制是一种隐式的重复代码。对于逻辑架构设计而言,机制是一种特殊的子系统,作为子系统的机制并不能“直接实现”软件的“最终功能”。在实现不同的最终功能时,可以重用同一个机制,避免重复进行繁琐的“组装”工作。
子系统划分的4个重要原则
l 职责不同的单元划归不同子系统(职责分离原则)
l 通用性不同的单元划归不同子系统(通用专用分离原则)
l 需要不同开发技能的单元划归不同子系统(技能分离原则)
l 兼顾工作量的相对平衡,进一步切分太大的子系统(工作量均衡原则)
对问题进行分解,分别解决小问题,其实这只是手段。合才是目的,不能“合”在一起支持更高层次功能模块,又有何用?
硬件强大了,但数据量在增加,计算复杂性也在提高,所以增加硬件未必能解决问题。增加硬件= 增加计算能力!= 软件的实际服务能力增强
物理架构视图着重考虑运行软件的计算机、网络、硬件设施等情况,还包括如何将软件包部署到这些硬件资源上,以及它们运行时的配置情况。由于一部分运行时质量属性需要硬件或网络的支持,所以物理架构必须关注如何配置硬件和网络来满足软件系统的可靠性、可伸缩性、持续可用性、性能、安全性等方面的要求。
物理架构设计有3项任务:
l 硬件的选择与物理拓扑
l 软件到硬件的映射关系
l 方案的优化
如果系统中,并不准备引入任何并行或并发处理,并且系统也没有基于SDK、API等基础软件进行定制开发,那就不须要设计运行架构的设计。
最常用于实际控制流的手段有3种:
l 进程
l 线程
l 中断服务程序
开发架构的设计应完成下列工作:
l 将“逻辑职责”映射为“程序单元”
Ø 要自主编写的源程序
Ø 可重用的库、框架
Ø 其他方式(如shell脚步、平台支持下的配置文件)
l 开发技术选型
Ø 开发语言
Ø 开发工具
l “程序单元”间关系
Ø Project划分
Ø Project目录结构
Ø 编译依赖关系
非功能需求的考虑是“贯穿环节”,在概念架构设计阶段,以及细化架构设计阶段都应重视。对大型系统而言,开发架构设计中的“Project划分”是不可或缺的。
确定数据分布方案是数据架构设计的难点。越是大系统,数据分布越关键。所谓分布式系统,不单单是程序的分布,还涉及数据的分布。常采用不同的数据分布存储与处理手段有:
Ø 独立Schema(Separate-schema)
Ø 集中(Centralized)
Ø 分区(Partitioned)
Ø 复制(Replicated)
Ø 子集(subset)
Ø 重组(Reorganized)
分区方式包括水平分区和垂直分区两种类型。水平分区的特点可以概括为“两个相同,两个不同”——相同的应用程序、不同的应用程序部署实例(instance)相同的数据模型,不同的数据值。在实践中,水平分区的应用非常广泛,而垂直分区的的作用相比之下要小得多。
在整个分布式系统中,数据保存多个副本,并且以某种机制(实时或快照)保持多个数据副本之间的数据一致性,这就是复制方式的数据分布策略。
“子集”是“复制”的特殊方式,就是某节点因功能或非功能考虑而保存全体数据的一个相对固定的子集。
重组这种数据分布策略,就是不同数据节点因要支持的功能不同,而以不同的Schema保存数据——但本质上这些数据是同源的。于是,重组策略要进行数据传递,而不是数据的“原样”复杂,而是以“重新组织”的格式进行传递或保存。根据典型的应用背景,重组分为两种类型:统计性重组和结构性重组。
非功能目标的方法论
软件架构设计为什么这么难?因为架构设计不是简单地处理“纯粹的技术问题”,而是要面对“技术与业务的关系问题”。最终,要求架构师不仅懂技术、懂业务,而且能够理顺复杂的技术和业务之间的关系。
从面向业务的需求,到最终的面向技术的软件系统,要跨越很大的鸿沟。软件架构设计就是要完成从面向业务到面向技术的转换,在鸿沟上架起一座桥梁。软件架构师根据各种需求进行架构设计,最终的软件架构包含了结构、协作、技术等方面的重要决策,为系统化的开发活动建立了基础。精通需求,是对架构师最基本要求之一,不了解需求是现在许多架构师职业发展道理上的瓶颈。值得强调的是,需求分析师和架构师这两个角色要掌握的需求知识并不完全一样。经典的观点师认为架构师应该掌握的需求知识师需求分析师的一个子集,其实不然。架构师必须懂需求。虽然无需研究诸如需求捕获等技术,但需求类型、需求影响架构的原理、质量属性间的相互影响关系都是必须精通的。
架构设计实践中,面对非功能需求目标时是容易犯“拍脑袋”这个经典错误的。非功能目标的设计是以场景技术为核心手段、以目标-场景-决策表为思维工具,致力于支撑非功能目标的理性设计过程。非功能需求不肯能是“速决战”,它必然是“持久战”,连编码都会影响到性能等非功能属性,更何况概念架构设计和细化架构设计呢?所以架构师必须注意,非功能目标的考虑是纵穿架构始终的环节。
软件的质量是设计出来的,这是公认的一个基本观点。实践中,架构师必须对场景进行评估,以决定是否支持这个场景。架构师经常要考虑的场景评估因素包括:
l 价值大小
l 代价大小
l 开发难度高低
l 技术趋势
最后,必须提醒的是“不支持某场景”恰恰是架构师的一种重要决策——如果每个场景都给予支持,理性设计就无从谈起,过度设计就在所难免了。