系统架构设计应考虑的问题
本⽂从程序的运⾏时结构和源代码的组织结构两个⽅⾯探讨了系统构架设计应考虑的各种因素,列举了系统构架设计⽂档应考虑的⼀些问题。
⼀、与构架有关的⼏个基本概念:
1、模块(module):⼀组完成指定功能的语句,包括:输⼊、输出、逻辑处理功能、内部信息、运⾏环境(与功能对应但不是⼀对⼀关系)。
2、组件(component):系统中相当重要的、⼏乎是独⽴的可替换部分,它在明确定义的构架环境中实现确切的功能。
3、模式(pattern):指经过验证,⾄少适⽤于⼀种实⽤环境(更多时候是好⼏种环境)的解决⽅案模板(⽤于结构和⾏为。在 UML 中:模式由参数化的协作来表⽰,但 UML 不直接对模式的其他⽅⾯(如使⽤结果列表、使⽤⽰例等,它们可由⽂本来表⽰)进⾏建模。存在各种范围和抽象程度的模式,例如,构架模式、分析模式、设计模式和代码模式或实施模式。模式将可以帮助我们抓住重点。构架也是存在模式的。⽐如,对于系统结构设计,我们使⽤层模式;对于分布式系统,我们使⽤代理模式(通过使⽤代理来替代实际的对象,使程序能够控制对该对象的访问);对于交互系统,我们使⽤MVC(M模型(对象)/V视
图(输出管理)/C控制器(输⼊处理))模式。模式是针对特定问题的解,因此,我们也可以针对需求的特点采⽤相应的模式来设计构架。
4、构架模式(architectural pattern):表⽰软件系统的基本结构组织⽅案。它提供了⼀组预定义的⼦系统、指定它们的职责,并且包括⽤于组织其间关系的规则和指导。
5、层(layer):对模型中同⼀抽象层次上的包进⾏分组的⼀种特定⽅式。通过分层,从逻辑上将⼦系统划分成许多集合,⽽层间关系的形成要遵循⼀定的规则。通过分层,可以限制⼦系统间的依赖关系,使系统以更松散的⽅式耦合,从⽽更易于维护。(层是对构架的横向划分,分区是对构架的纵向划分)。
6、系统分层的⼏种常⽤⽅法:
1)常⽤三层服务:⽤户层、业务逻辑层、数据层;
2)多层结构的技术组成模型:表现层、中间层、数据层;
3)⽹络系统常⽤三层结构:核⼼层、汇聚层和接⼊层;
4) RUP典型分层⽅法:应⽤层、专业业务层、中间件层、系统软件层;
5)基于Java的B/S模式系统结构:浏览器端、服务器端、请求接收层、请求处理层;
6)某六层结构:功能层(⽤户界⾯)、模块层、组装层(软件总线)、服务层(数据处理)、数据层、核⼼层;
7、构架(Architecture,愿意为建筑学设计和建筑物建造的艺术与科学): 在RUP中的定义:软件系统的构架(在某⼀给定点)是指系统重要构件的组织或结构,这些重要构件通过接⼝与不断减⼩的构件与接⼝所组成的构件进⾏交互;《软件构架实践》中的定义:某个软件或者计算系统的软件构架即组成该系统的⼀个或者多个结构,他们组成软件的各个部分,形成这些组件的外部可见属性及相互间的联系;IEEE 1471-2000中的定义:the fundamental organization of a system emboided in its components,their relationships to each other,and to the enviroment and the principles guiding its design and evolution,构架是系统在其所处环境中的最⾼层次的概念。软件系统的构架是通过接⼝交互的重要构件(在特定时间点)的组织或结构,这些构件⼜由⼀些更⼩的构件和接⼝组成。(“构架”可以作为名词,也可作为动词,作为动词的“构架”相当于“构架设计”)
8、构架的描述⽅式:“4+1”视图(⽤例视图、设计视图、实现视图、过程视图、配置视图)是⼀个被⼴为使⽤的构架描述的模型;RUP 过程的构架描述模板在“4+1”视图的基础上增加了可选的数据视图(从永久性数据存储⽅⾯来对系统进⾏说明);HP公司的软件描述模板也是基于“4+1”视图。
9、结构:软件构架是多种结构的体现,结构是系统构架从不同⾓度观察所产⽣的视图。就像建筑物的结构会随着观察动机和出发点的不同⽽有多种含义⼀样,软件构架也表现为多种结构。常见的软件结构有:模块结构、逻辑或概念结构、进程或协调结构、物理结构、使⽤结构、调⽤结构、数据流、控制流、类结构等等。
⼆、构架设计应考虑的因素概揽:
模块构架设计可以从程序的运⾏时结构和源代码的组织结构⽅⾯考虑。
1、程序的运⾏时结构⽅⾯的考虑:
1)需求的符合性:正确性、完整性;功能性需求、⾮功能性需求;
2)总体性能(内存管理、数据库组织和内容、⾮数据库信息、任务并⾏性、⽹络多⼈操作、关键算法、与⽹络、硬件和其他系统接⼝对性能的影响);
3)运⾏可管理性:便于控制系统运⾏、监视系统状态、错误处理;模块间通信的简单性;与可维护性不同;
4)与其他系统接⼝兼容性;
5)与⽹络、硬件接⼝兼容性及性能;
6)系统安全性;
7)系统可靠性;
8)业务流程的可调整性;
9)业务信息的可调整性
10)使⽤⽅便性
11)构架样式的⼀致性
注:运⾏时负载均衡可以从系统性能、系统可靠性⽅⾯考虑。
2、源代码的组织结构⽅⾯的考虑:
1)开发可管理性:便于⼈员分⼯(模块独⽴性、开发⼯作的负载均衡、进度安排优化、预防⼈员流动对开发的影响)、利于配置管理、⼤⼩的合理性与适度复杂性;
2)可维护性:与运⾏可管理性不同;
3)可扩充性:系统⽅案的升级、扩容、扩充性能;
4)可移植性:不同客户端、应⽤服务器、数据库管理系统;
5)需求的符合性(源代码的组织结构⽅⾯的考虑)。
三、程序的运⾏时结构⽅⾯的考虑:
1、需求的符合性:正确性、完整性;功能性需求、⾮功能性需求
软件项⽬最主要的⽬标是满⾜客户需求。在进⾏构架设计的时候,⼤家考虑更多的是使⽤哪个运⾏平台、编成语⾔、开发环境、数据库管理系统等问题,对于和客户需求相关的问题考虑不⾜、不够系统。如果⽆论怎么好的构架都⽆法满⾜客户明确的某个功能性需求或⾮功能性需求,就应该与客户协调在项⽬范围和需求规格说明书中删除这⼀需求。否则,架构设计应以满⾜客户所有明确需求为最基本⽬标,尽量满⾜其隐含的需求。(客户的⾮功能性需求可能包括接⼝、系统安全性、可靠性、移植性、扩展性等等,在其他⼩节中细述)
⼀般来说,功能需求决定业务构架、⾮功能需求决定技术构架,变化案例决定构架的范围。需求⽅⾯的知识告诉我们,功能需求定义了软件能够做些什么。我们需要根据业务上的需求来设计业务构架,以使得未来的软件能够满⾜客户的需要。⾮功能需求定义了⼀些性能、效率上的⼀些约束、规则。⽽我们的
技术构架要能够满⾜这些约束和规则。变化案例是对未来可能发⽣的变化的⼀个估计,结合功能需求和⾮功能需求,我们就可以确定⼀个需求的范围,进⽽确定⼀个构架的范围。(此段 From林星)
这⾥讲⼀个前⼏年因客户某些需求错误造成构架设计问题⽽引起系统性能和可靠性问题的⼩⼩的例⼦:此系统的需求本⾝是⽐较简单的,就是将某城市的某业务的全部历史档案卡⽚扫描存储起来,以便可以按照姓名进⾏查询。需求阶段客户说卡⽚⼤约有20万张,需求调研者出于对客户的信任没有对数据的总量进⾏查证。由于是中⼩型数据量,并且今后数据不会增加,经过计算20万张卡⽚总体容量之后,决定使⽤⼀种可以单机使⽤也可以联⽹的中⼩型数据库管理系统。等到系统完成开始录⼊数据时,才发现数据⾄少有60万,这样使⽤那种中⼩型数据库管理系统不但会造成系统性能的问题,⽽且其可靠性是⾮常脆弱的,不得不对系统进⾏重新设计。从这个⼩⼩的教训可以看出,需求阶段不仅对客户的功能需求要调查清楚,对于⼀些隐含⾮功能需求的⼀些数据也应当调查清楚,并作为构架设计的依据。
对于功能需求的正确性,在构架设计⽂档中可能不好验证(需要⼈⼯、费⼒)。对于功能需求完整性,就应当使⽤需求功能与对应模块对照表来跟踪追溯。对于⾮功能需求正确性和完整性,可以使⽤需求⾮功能与对应设计策略对照表来跟踪追溯评估。
“软件设计⼯作只有基于⽤户需求,⽴⾜于可⾏的技术才有可能成功。”
2、总体性能
性能其实也是客户需求的⼀部分,当然可能是明确的,也有很多是隐含的,这⾥把它单独列出来在说明⼀次。性能是设计⽅案的重要标准,性能应考虑的不是单台客户端的性能,⽽是应该考虑系统总的综合性能;
性能设计应从以下⼏个⽅⾯考虑:内存管理、数据库组织和内容、⾮数据库信息、任务并⾏性、⽹络多⼈操作、关键算法、与⽹络、硬件和其他系统接⼝对性能的影响;
⼏点提⽰:算法优化及负载均衡是性能优化的⽅向。经常要调⽤的模块要特别注意优化。占⽤内存较多的变量在不⽤时要及时清理掉。需要下载的⽹页主题⽂件过⼤时应当分解为若⼲部分,让⽤户先把主要部分显⽰出来。
3、运⾏可管理性
系统的构架设计应当为了使系统可以预测系统故障,防患于未然。现在的系统正逐步向复杂化、⼤型化发展,单靠⼀个⼈或⼏个⼈来管理已显得⼒不从⼼,况且对于某些突发事件的响应,⼈的反应明显不够。因此通过合理的系统构架规划系统运⾏资源,便于控制系统运⾏、监视系统状态、进⾏有效的错误处理;为了实现上述⽬标,模块间通信应当尽可能简单,同时建⽴合理详尽的系统运⾏⽇志,系统通过⾃动审计运⾏⽇志,了解系统运⾏状态、进⾏有效的错误处理;(运⾏可管理性与可维护性不同)
4、与其他系统接⼝兼容性(解释略)
5、与⽹络、硬件接⼝兼容性及性能(解释略)
6、系统安全性
随着计算机应⽤的不断深⼊和扩⼤,涉及的部门和信息也越来越多,其中有⼤量保密信息在⽹络上传输,所以对系统安全性的考虑已经成为系统设计的关键,需要从各个⽅⾯和⾓度加以考虑,来保证数据资料的绝对安全。
7、系统可靠性
系统的可靠性是现代信息系统应具有的重要特征,由于⼈们⽇常的⼯作对系统依赖程度越来越多,因此系统的必须可靠。系统构架设计可考虑系统的冗余度,尽可能地避免单点故障。系统可靠性是系统在给定的时间间隔及给定的环境条件下,按设计要求,成功地运⾏程序的概率。成功地运⾏不仅要保证系统能正确地运⾏,满⾜功能需求,还要求当系统出现意外故障时能够尽快恢复正常运⾏,数据不受破坏。
8、业务流程的可调整性
应当考虑客户业务流程可能出现的变化,所以在系统构架设计时要尽量排除业务流程的制约,即把流程中的各项业务结点⼯作作为独⽴的对象,设计成独⽴的模块或组件,充分考虑他们与其他各种业务对象模块或组件的接⼝,在流程之间通过业务对象模块的相互调⽤实现各种业务,这样,在业务流程发⽣有
限的变化时(每个业务模块本⾝的业务逻辑没有变的情况下),就能够⽐较⽅便地修改系统程序模块或组件间的调⽤关系⽽实现新的需求。如果这种调⽤关系被设计成存储在配置库的数据字典⾥,则连程序代码都不⽤修改,只需修改数据字典⾥的模块或组件调⽤规则即可。
9、业务信息的可调整性
应当考虑客户业务信息可能出现的变化,所以在系统构架设计时必须尽可能减少因为业务信息的调整对于代码模块的影响范围。
10、使⽤⽅便性
使⽤⽅便性是不须提及的必然的需求,⽽使⽤⽅便性与系统构架是密切相关的。WinCE(1.0)的失败和后来改进版本的成功就说明了这个问题。 WinCE(1.0)有太多层次的视窗和菜单,⽽⽤户则更喜欢简单的界⾯和快捷的操作。失败了应当及时纠正,但最好不要等到失败了再来纠正,这样会浪费巨⼤的财⼒物⼒,所以在系统构架阶段最好能将需要考虑的因素都考虑到。当然使⽤⽅便性必须与系统安全性协调平衡统⼀,使⽤⽅便性也必须与业务流程的可调整性和业务信息的可调整性协调平衡统⼀。“满⾜⽤户的需求,便于⽤户使⽤,同时⼜使得操作流程尽可能简单。这就是设计之本。”
11、构架样式的⼀致性
软件系统的构架样式有些类似于建筑样式(如中国式、哥特式、希腊复古式)。软件构架样式可分为数据流构架样式、调⽤返回构架样式、独⽴组件构架样式、以数据为中⼼的构架样式和虚拟机构架样式,每⼀种样式还可以分为若⼲⼦样式。构架样式的⼀致性并不是要求⼀个软件系统只能采⽤⼀种样式,就像建筑样式可以是中西结合的,软件系统也可以有异质构架样式(分为局部异质、层次异质、并⾏异质),即多种样式的综合,但这样的综合应该考虑其某些⽅⾯的⼀致性和协调性。每⼀种样式都有其使⽤的时机,应当根据系统最强调的质量属性来选择。
四、源代码的组织结构⽅⾯的考虑:
1、开发可管理性
便于⼈员分⼯(模块独⽴性、开发⼯作的负载均衡、进度安排优化、预防⼈员流动对开发的影响:⼀个好的构架同时应有助于减少项⽬组的压⼒和紧张,提⾼软件开发效率)、利于配置管理、⼤⼩的合理性、适度复杂性;
1)便于⼈员分⼯-模块独⽴性、层次性
模块独⽴性、层次性是为了保证项⽬开发成员⼯作之间的相对独⽴性,模块联结⽅式应该是纵向⽽不是横向, 模块之间应该是树状结构⽽不是⽹状结构或交叉结构,这样就可以把开发⼈员之间的通信、模块
开发制约关系减到最少。同时模块独⽴性也⽐较利于配置管理⼯作的进⾏。现在有越来越多的的软件开发是在异地进⾏,⼀个开发组的成员可能在不同城市甚⾄在不同国家,因此便于异地开发的⼈员分⼯与配置管理的源代码组织结构是⾮常必要的。
2)便于⼈员分⼯-开发⼯作的负载均衡
不仅仅是开发出来的软件系统需要负载均衡,在开发过程中开发⼩组各成员之间⼯作任务的负载均衡也是⾮重要的。所谓⼯作任务的负载均衡就是通过合理的任务划分按照开发⼈员特点进⾏分配任务,尽量让项⽬组中的每个⼈每段时间都有⽤武之地。这就需要在构架设计时应当充分考虑项⽬组⼿头的⼈⼒资源,在实现客户需求的基础上实现开发⼯作的负载均衡,以提⾼整体开发效率。
3)便于⼈员分⼯-进度安排优化;
进度安排优化的前提是模块独⽴性并搞清楚模块开发的先后制约关系。利⽤⼯作分解结构对所有程序编码⼯作进⾏分解,得到每⼀项⼯作的输⼊、输出、所需资源、持续时间、前期应完成的⼯作、完成后可以进⾏的⼯作。然后预估各模块需要时间,分析各模块的并⾏与串⾏(顺序制约),绘制出⽹络图,出影响整体进度的关键模块,算出关键路径,最后对⽹络图进⾏调整,以使进度安排最优化。
有个家喻户晓的智⼒题叫烤⾁⽚策略:约翰逊家户外有⼀个可以同时烤两块⾁⽚的烤⾁架,烤每块⾁⽚
的每⼀⾯需要10分钟,现要烤三块⾁⽚给饥肠辘辘急不可耐的⼀家三⼝。问题是怎样才能在最短的时间内烤完三⽚⾁。⼀般的做法花20分钟先烤完前两⽚,再花20分钟烤完第三⽚。有⼀种更好的⽅法可以节省10分钟,⼤家想想。
4)便于⼈员分⼯-预防员⼯⼈员流动对开发的影响
⼈员流动在软件⾏业是司空见惯的事情,已经是⼀个常见的风险。作为对这⼀风险的有效的防范对策之⼀,可以在构架设计中考虑到并预防员⼯⼈员流动对开发的影响。主要的思路还是在模块的独⽴性上(追求⾼内聚低耦合),组件化是⽬前流⾏的趋势。
5)利于配置管理(独⽴性、层次性)
利于配置管理与利于⼈员分⼯有⼀定的联系。除了逻辑上的模块组件要利于⼈员分⼯外,物理上的源代码层次结构、⽬录结构、各模块所处源代码⽂件的部署也应当利于⼈员分⼯和配置管理。(尽管现在配置管理⼯具有较强⼤的功能,但⼀个清楚的源码分割和模块分割是⾮常有好处的)。
6)⼤⼩的合理性与适度复杂性
⼤⼩的合理性与适度复杂性可以使开发⼯作的负载均衡,便于进度的安排,也可以使系统在运⾏时减少不必要的内存资源浪费。对于代码的可阅读性和系统的可维护性也有⼀定的好处。另外,过⼤的模块常
常是系统分解不充分,⽽过⼩的模块有可能降低模块的独⽴性,造成系统接⼝的复杂。
2、可维护性
便于在系统出现故障时及时⽅便地到产⽣故障的原因和源代码位置,并能⽅便地进⾏局部修改、切割;(可维护性与运⾏可管理性不同)
3、可扩充性:系统⽅案的升级、扩容、扩充性能
系统在建成后会有⼀段很长的运⾏周期,在该周期内,应⽤在不断增加,应⽤的层次在不断升级,因此采⽤的构架设计等⽅案因充分考虑升级、扩容、扩充的可⾏性和便利
4、可移植性
不同客户端、应⽤服务器、数据库管理系统:如果潜在的客户使⽤的客户端可能使⽤不同的操作系统或浏览器,其可移植性必须考虑客户端程序的可移植性,或尽量不使业务逻辑放在客户端;数据处理的业务逻辑放在数据库管理系统中会有较好的性能,但如果客户中不能确定使⽤的是同⼀种数据库管理系统,则业务逻辑就不能数据库管理系统中;
达到可移植性⼀定要注重标准化和开放性:只有⼴泛采⽤遵循国际标准,开发出开放性强的产品,才可
以保证各种类型的系统的充分互联,从⽽使产品更具有市场竞争⼒,也为未来的系统移植和升级扩展提供了基础。
5、需求的符合性
从源代码的组织结构看需求的符合型主要考虑针对⽤户需求可能的变化的软件代码及构架的最⼩冗余(同时⼜要使得系统具有⼀定的可扩展性)。
五、写系统构架设计⽂档应考虑的问题
构架⼯作应该在需求开发完成约80%的时候开始进⾏,不必等到需求开发全部完成,需要项⽬经理以具体的判断来评估此时是否⾜以开始构建软件构架。
给出⼀致的轮廓:系统概述。⼀个系统构架需要现有概括的描述,开发⼈员才能从上千个细节甚⾄数⼗个模块或对象类中建⽴⼀致的轮廓。
构架的⽬标应该能够清楚说明系统概念,构架应尽可能简化,最好的构架⽂件应该简单、简短,清晰⽽不杂乱,解决⽅案⾃然。
构架应单先定义上层的主要⼦系统,应该描述各⼦系统的任务,并提供每个⼦系统中各模块或对象类的的初步列表。
构架应该描述不同⼦系统间相互通信的⽅式,⽽⼀个良好的构架应该将⼦系统间的通信关系降到最低。
成功构架的⼀个重要特⾊,在于标明最可能变更的领域,应当列出程序中最可能变更的部分,说明构架的其他部分如何应变。
源程序是指什么程序复⽤分析、外购:缩短软件开发周期、降低成本的有效⽅案未必是⾃⾏开发软件,可以对现有软件进⾏复⽤或进⾏外购。应考虑其对构架的影响。
除了系统组织的问题,构架应重点考虑对于细节全⾯影响的设计决策,深⼊这些决策领域:外部软件接⼝(兼容性、通信⽅式、传递数据结构)、⽤户接⼝(⽤户接⼝和系统层次划分)、数据库组织和内容、⾮数据库信息、关键算法、内存管理(配置策略)、并⾏性、安全性、可移植性、⽹络多⼈操作、错误处理。
要保证需求的可追踪性,即保证每个需求功能都有相应模块去实现。
构架不能只依据静态的系统⽬标来设计,也应当考虑动态的开发过程,如⼈⼒资源的情况,进度要求的情况,开发环境的满⾜情况。构架必须⽀持阶段性规划,应该能够提供阶段性规划中如何开发与完成的⽅式。不应该依赖⽆法独⽴运⾏的⼦系统构架。将系统各部分的、依赖关系出来,形成⼀套开发计划。
六、结语
系统构架设计和千差万别的具体的开发平台密切相关,因此在此⽆法给出通⽤的解决⽅案,主要是为了说明哪些因素是需要考虑的。对于每个因素的设计策略和本⽂未提到的因素需要软件构架设计师在具体开发实践中灵活把握。不同因素之间有时是⽭盾的,构架设计时需要根据具体情况进⾏平衡。
参考⽂献
《软件构架实践》SEI软件⼯程译丛,林·巴斯著
《微软项⽬:求⽣法则》Steve McConnell著,余孟学译
《实⽤软件⼯程》第⼆版,郑⼈杰、殷⼈昆、陶永雷等著
《软件⼯程:实践者的研究⽅法》(第5版)Roger S.Pressman著
《软件开发的科学与艺术》陈宏刚等著