文档开发者中心
我的

浅谈架构设计(中)

上篇文章“浅谈架构设计(上)”主要参考了《软件构建实践(第2版)》和一些前辈的经验总结,谈了架构设计的一些定义、作用以及表现形式,相当于架构设计的“形”,但是架构师在做实际的架构设计时,背后的思考逻辑是什么,形成这些成果的过程中是怎样考虑的,这些问题在上篇文章中并不太容易看出。


在本文中,我想通过讨论若干问题,来说明我所认为的架构设计工作背后的思考逻辑,这其中既有几位前辈架构师的观点,也有我自己的经验之谈。(特别说明:架构师要关注的东西很多,我这里挂一漏万,在本文中没有提及的,并不代表不重要;我之所以强调下文中的若干问题,是因为我认为这些问题对我当前所在领域的架构设计工作具有指导意义。)


1、架构设计和架构师的目标


想清楚架构设计所追求的目标是什么,这一点很重要。对于架构师自己来说,心里要清楚,架构设计所追求的是什么,架构设计要解决哪些问题,架构设计要关注哪些因素?对于组织和团队来讲,要知道对架构师的要求是什么,出了哪些问题后需要通过架构设计来解决、而不是其他什么手段。


在系统设计与架构设计的对比中已经指出,架构设计是为了“能够给出一个框架能够更好的实现所有需求”,那么怎么理解这个“更好”?下面分几点说说我的理解。


1.1 技术上,好的架构要能够保证系统满足相应质量属性的目标要求。


在《软件构建实践(第2版)》中指出,架构阻止或支持软件系统的质量属性实现。用户对质量属性等级的期望和/或设计师所选择的质量属性的等级是架构早期的关键决策的输入,也就是说,质量属性的需求影响着架构的选择、现有技术选择等关键技术决策。而一旦架构的关键决策完成了,架构一旦成形,那么这个系统所能达到的质量属性的等级几乎就已经确定了,如果想在此架构基础上提升质量属性等级,往往意味着较高的代价甚至只能调整架构。


当然有一点需要说明的是,有些质量属性之间是相互制约的,架构并不能使所有方面的质量属性需求都得到满足。所以架构设计的一个关键问题是要对该系统的质量属性需求进行识别和权衡,要选择出若干个最看重的质量属性需求,然后以此为目标进行架构设计。


综上,应当在架构设计过程中选中若干关键的质量属性需求,将其视为整个系统的质量属性目标,所设计出来的架构应当满足这些质量属性的要求。这应当是架构设计的最基本要求。


1.2 管理上,好的架构能够支持将一个复杂系统的开发工作分解为若干个小组工作,并使开发工作更容易、更有效率。


在第1条确定了技术目标后,接下来是达成该技术目标的开发过程。


好的架构首先能够支撑对整个开发工作的分解,以便于分工到各小组进行开发。大家都知道架构是对复杂系统进行模块化分解,分工的基础是模块划分;那么模块的功能单一、内聚和边界清楚,就决定着该模块的开发小组的职责单一、沟通界面清楚。


如果模块的功能不单一,那么就对这个开发小组的人员能力要求多,工作界面宽,工作流程也很难标准化。长此以往会带来团队建设和人员稳定的问题:人员的工作能力构建成本高,尤其是新员工需要花更多时间来学习和了解,人员替换成本高,团队成员需要在不同工作间切换,效率低、感觉累。“模块功能单一”在组织层面的好处是降低了对人员的技能要求。


如果模块的功能边界不清楚,那么就意味着模块之间的功能定位不清晰,当设计一个新功能时常常造成开发人员的困惑和纠结。比如对于一个新功能需求,不知道是该在模块A做还是模块B做,或者不知道这个新功能该如何在模块A和B之间做分解,最终的结果就是模块A的开发小组与模块B的开发小组各持己见、工作迟迟无法开展;对两个开发小组来讲,如果没有明确的架构原则来支撑,工作量常常就成为最主要的考量因素,让谁做都觉得自己吃亏;相互都说服不了对方的最后结果就是上架构评审会决策。“大事小事都要上架构评审会、不上会就无法分解工作,架构师忙于决策各种问题”是模块边界不清楚的另一个具体表现。


虽然以上这些问题不可能绝对的完全避免,但是一个好的架构应当致力于尽量避免这些问题:


当遇到一个具有通用意义的新需求出现分解上的分歧时,要讨论这类需求对架构的影响:或者修正架构原则、以确保这类需求在后续再次出现时能够快速达成一致而无需再决策;或者进行架构变更,以使架构能够满足这类新需求的开发,目标仍然是当该类需求再次出现时能够快速达成一致。


因此,一个模块边界定义清晰的架构,可以使各开发小组更容易的对功能进行分解,确定自己的工作并聚焦,从而避免开发小组间的扯皮和减少上会决策,提高开发效率。对一个架构师来说,应当致力于搭出这样一个利于工作分解、减少分歧的架构,并让各个开发小组明白无误的理解它,以便使各个开发小组能够有序的并行工作并支撑系统目标的最终达成,即使出现一些分歧,也能够快速决策、提高整体工作效率。


模块化的架构设计带来的另外一个好处是并行开发和渐增构建。模块边界清晰后,两个开发小组可以快速对功能进行分解并确定模块间的接口是否受影响,若不受影响则可直接并行开发,若需变更接口,则确定接口后两个小组的开发工作可以并行而不相互依赖,等到系统集成测试时做联调就可以。渐增构建可以使开发团队逐步构建和交付系统,降低整体工作复杂度和风险,实现渐增构建的前提是模块间的依赖关系要明确,尤其不能有环形依赖。并行开发和渐增构建带来的好处是开发和交付效率的提升。


本文系转载,作者:HW-YB

篇幅过长,楼下继续~

点赞
2
收藏
2
回复
8
分享
举报
浏览1616 发布于2019-08-09 02:03未知归属地
全部评论
最多点赞
最新发布
最早发布

嘿嘿,我是来这里学习的。


8楼回复于2019-08-09 09:51 未知归属地

大神,多发点这样的帖子啊,急需


7楼回复于2019-08-09 09:49 未知归属地

文字很多,但绝对的干货啊!!!

6楼回复于2019-08-09 06:37 未知归属地

2.2 可扩展性的陷阱:过设计


如果为了一些不必要的场景而做了可扩展性设计,浪费了工作投入,还可能会增加未来学习、沟通或者管理上的难度,那么就属于“过设计”。


比如上文中提到,过度的抽象不可取。我遇到的一个实例:在关系型Oracle数据库中,为了增强表结构的扩展性,有人建议将表结构设计为Key-Value的形式,这种设计的确可以提高扩展性,但是随之而来的破坏了关系型数据库的基本结构,带来的是查询上的不便以及性能等一系列问题。


如之前章节所述,架构师做架构设计的初衷是为了付出一些架构成本,来保证系统的可扩展性,从而达到总体上成本最优的目的。评估一个架构设计是否合理、或者是否值得做,需要对比这个架构设计所付出的成本与未来带来的收益孰轻孰重。但架构成本既有显性的(比如可以直接体现为代码行的开发工作)、也有隐性的(比如学习、沟通、或管理上的);显性的成本容易衡量,但隐性成本就很难量化,而且往往带有个人的主观意见。对于可扩展性带来的“未来的收益”,就更加难以确定:首先这个收益实际上是一个对未来的估计和判断,是否一定会发生,本来就带有个人的主观判断,第二,即使发生了,收益有多大,经常也很难量化,这些收益也同样有显性的(比如开发工作量下降)和隐性的(比如管理成本降低、沟通效率提升等)。因此,要对比“架构设计所付出的成本与未来带来的收益孰轻孰重”,实际上是很难的,这也是平时工作中最容易有争论的地方。


架构师在平常工作中,经常就“某某设计是否合理”的问题与设计师、开发人员进行讨论和PK,架构师通常要坚持自己的观点去说服别人,在有些情况下也容易由于“过于固执己见”而陷入“过设计”的误区。怎么能避免呢?我也没什么好办法,唯一的建议就是架构师需要仔细倾听设计师和开发人员的意见,兼听则明,固执己见则暗,在争论久持不下的时候也恰恰意味着架构的成本和其带来的收益的性价比不高,或者即使不做这个设计所造成的问题也不严重、是可接受的,所以在架构上妥协一下也未尝不可。这是灰度的艺术。(架构设计既要能在总体上坚持原则,又要能在适当时候灰度妥协,这的确很难)

5楼回复于2019-08-09 06:36 未知归属地

2.1 架构的成本


“架构成本”的最简单例子就是接口封装:对于首次开发来讲,接口封装的实现一般都比直接实现这个功能要多投入一些工作量,但是接口封装的好处是为后续的功能扩展带来好处(可将一些新增的功能需求的影响封装在模块内部)、降低了工作量,长远来看,接口封装的一次性投入带来后续功能扩展的工作量降低,所以这个投入是划算的。反例:如果两个模块联系很紧密,即使将两个模块之间的接口进行了封装,但是每次新增功能都需要修改这个封装接口,那么这个封装接口的工作量就不划算,本质上是反映出来这个架构设计不合理,要么是这两个模块之间的功能定位没切分清楚,要么是这两个模块应该合并为一模块。


复杂一点的例子:一个系统有两个模块A和B,现在要新增一个功能,A和B都可实现,如果放在A模块实现、比较符合A模块的功能定位、但工作量较大,如果放在B模块实现,不符合模块定位但工作量小,这时选择放在A还是选择放在B?架构上应该选择A,虽然A工作量大,但是符合A的定位,后续更好扩展和维护,总体来看工作量更小。当然还一种情况时,放在A的工作量太大以至于难以接受,那就得考虑架构变更,是否考虑将该新增功能放在B模块实现、并修改A、B模块的定位,以实现自洽。


更复杂的例子是抽象。抽象也是增强扩展性的一种手段(下面章节会详细说明),但抽象的设计在第一次实现时的工作量肯定是相对更大一点,它的目标是便于后续的扩展、使之在后续新增功能时的开发工作量较小。但抽象带来的负面作用是复杂度增加,意味着学习成本和信息传递的成本增加(也可以叫做开发难度的增加),这些成本是隐性的、不直接体现为开发代码的工作量,但的的确确占用着开发人员的工作时间,因此也应当属于架构成本。抽象要适度,过度的抽象也是不可取的。


综上,架构需要在系统的整个生命周期中产生作用,架构的扩展性应该保证:在不断开发新功能的过程中,通过付出一定的架构成本,仍能使整个系统能够达成所期望的质量属性目标,并使总体上的开发成本和管理成本最低。

4楼回复于2019-08-09 06:35 未知归属地

2、架构的可扩展性设计


ZWS在《架构与模块设计》中指出:软件架构设计的焦点在于可扩展性,为了达到此目的,甚至愿意付出性能的些微下降来达到总体性能的不变,因为通常硬件性能一直在提升。


实际上,架构设计在考虑扩展性时,除了性能些微降低之外,还愿意额外进行一些设计和些许工作量,来保证整个架构的稳定和模块边界的清晰,从而使该系统在后续还能够持续的简单的添加新功能需求,使系统在生命周期内的总体上的工作量最低(这里的工作量不仅仅是包含开发工作量,还包含信息传递和沟通等隐性的管理成本)。为架构设计的长期可扩展性所付出的额外代价,只要这个代价与带来的收益相比具有好的性价比,有助于降低总体上的工作量,那么这个代价就是值得的,我将这个代价称为“架构的成本”。

3楼回复于2019-08-09 06:34 未知归属地

1.4 小结


综上,架构师需要在系统的整个生命周期内来综合考虑、权衡方案,以保证系统的架构稳定和高质量、有效的交付。架构师要对产品的生命周期内的架构设计负总责:在产品第一个版本开发时,架构设计很重要,在后续维护过程中的架构的看护和演进工作也很重要。这就要求架构师不但不能短视,还必须要识别短视的设计、并与其斗争。

2楼回复于2019-08-09 06:33 未知归属地

1.3 在时间维度上,好的架构要能够在系统的整个生命周期内都能达到以上两个目标


一般来说,软件系统的成本大约有80%发生在初次部署之后的维护阶段,发生的开发工作大部分在这个阶段,也就是说,大部分设计和开发人员的大部分工作都是在已有系统或代码的基础上工作而不是全新的开发。这个道理应该算是软件工程领域中的常识,但随着系统的不断演进和新需求的不断开发,系统可能会变得越来越复杂、越来越笨重,新需求的开发和现有功能的维护工作量逐渐攀升,进而占用很多开发人力。


在《网络能源OSS开发部-架构设计原则》中给出了“设计腐烂”的现象和如下4种征兆:由不明显并不被管理者关注,最后演变到系统最简单的修改都需要付出很大代价,最后管理者感知深陷开发泥沼,整天陷入问题单海洋中。


l 僵化:软件变得越来越难于改变,甚至简单的改变也很困难。每次改变都导致后续一系列依赖模块的改变。从开始简单的花两天对一个模块的修改到数周一个模块一个模块持续的修改,工程师要在应用程序中跟踪改变的线索。当管理者的这种害怕变得非常厉害,他们会拒绝允许对软件进行修改,正式的僵化就开始了。设计的不足会引起管理策略的不利。


l 脆弱:与僵化紧密联系的就是脆弱。脆弱就是这样一种趋势:每次修改后,软件的很多地方被破坏。这种破坏通常发生在与被修改的地方在概念上没有关系的地方。这种现象往往体现在层出不穷的问题单,往往让管理者对系统的稳定性感觉茫然,脆弱往往打击项目经理对系统质量的信心。


l 硬化:硬化就是无法重用已有的组件。当某个工程师发现他要的模块与另一个工程师写的模块类似时,这种情况通常就发生了。而且,当模块有太多依赖的包袱时,这种情况也会发生。往往设计者或开发者希望重新写一块代码“另起炉灶”的心理使得系统更加庞大,最终没有谁愿意去重构。


l 粘性:粘性有两种形式:设计的粘性和环境的粘性。当正式的方案和零散的方案混在一起,设计者对归并到正式方案感觉非常难以入手时,继续另起零散方案的粘性设计就出现了。环境粘性是,当开发者面对修改任意一点就引发大量编译时,开发者不得不面对小的修改就耗用大量时间在编译和代码提交上时,这就是环境的粘性。


防止上述情形发生的方法,是需要在架构上通过某种原则控制模块间的依赖、不允许任其不断衍生。


综上,架构设计不能短视。架构设计“能够给出一个框架能够更好的实现所有需求”,这里的“所有”,不仅包含当前系统可见的需求,也要包含未来的可能发生的需求。换句话说,架构设计在完成功能目标和质量属性的前提下,不能仅仅考虑当前的成本最低,而是要追求在系统在整个生命周期内的成本最低。


这个目标的达成,一方面要依靠架构师在设计之初就要考虑通过一些架构原则来控制模块间的依赖,另一方面,还需要架构师在后续的系统演进过程中能够看护整个架构。对于后者,还有两种情况:对于需求或者设计不合理、违背了架构原则的情况,架构师要敢于坚持原则,引导需求、指导正确的设计;对于需求本身合理、但是架构需演进的场景,架构师要善于了解需求和对需求进行归类,对架构原则进行演进,以便适应新的场景需要。在看护架构的过程中,架构师既要能坚持原则,又要避免僵化执行。

1楼编辑于2019-08-09 06:33 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。