前端架构:从入门到微前端
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 架构设计原则

不同的人在设计架构时会出现不同的风格,在细节的把握上也会出现特有的风格,这便是架构的设计原则。笔者根据自己的项目经验,总结了三个设计原则,如下。

◎ 不多也不少:不做多余的设计,也不缺少关键的部分。

◎ 演进式:不断地演进以使架构适应当前的环境。

◎ 持续性:长期的架构改进比什么都重要。

它们不是真实的架构需求,而是隐藏在背后的设计思想,也是不同人的设计价值观。

1.3.1 不多也不少

一个好的架构设计,内容不多也不少。增加新的元素,觉得有些多余;想删除某个元素,却又找不到更合适的。无论是艺术设计领域,还是计算机领域,原则都是相似且适用的。

设计过多则过度设计。即我们针对未来,提前准备好相应的设计。这些设计都是假想出来的,在实现的时候设计本身便不再适用。反而,我们还要解决之前的设计带来的问题,重新修改、删除原先的设计,它们会进一步增加项目的成本。不过,过度设计多出自规模较大的项目,参与人数众多,某些地方如果不能进行预先设计,那么在实践过程中会遇到一系列问题。

设计过少则设计不足。设计不足会使架构扩展性不强,不能灵活地应对变化。值得一提的是,设计过多有可能是设计者的癖好或是炫技,而设计不足则可能是能力有限。

对于过度设计或者设计不足,往往很难做到真正的平衡及适宜。尤其在出现过其中一个问题之后,很容易往另外一个极端发展。如我们在某个项目里设计过度,在下一个相似的项目里,就可能会因为刻意减少设计而导致设计不足;相似的,如果在某个项目里缺少了设计,那么在下一个项目里,可能又会出现过度设计。

此外,我们还经常作两种假设,一是未来的人会比我们聪明,所以少做了一些设计;二是未来的接手者会比我们略逊一点点,那么就会为他们多做一些设计。

对于低级别的设计来说,应尽量避免进入细节设计,宝贵的资源应该投入更紧迫的业务开发中。对于高级别的设计来说,不预先设计模块和组件,仅从架构上考虑,即为未来预留扩展的空间。比如对于前端开发而言,为了将来在内部开发自己的组件库,当前可以通过适配者、代理模式对第三方组件进行二次封装。

如果在代码中为未来的代码预留一定的空间,那么大多是会产生问题的。因为多余的设计,会影响系统的后续扩展,并且在修改相关代码时,不敢放手去改,以满足现在的需求。比如在设计前端组件的过程中,想到未来会添加某些功能,便预留相关的接口,便有过度设计的嫌疑。

架构都只是适合当前的情况,一谈论到各种需求变化时,会发现架构设计上有各种不足,这并非是架构的问题。架构要不断根据需求演进变化,以满足新的需求。

1.3.2 演进式

适应环境能够生存下来的物种,并不是那些最强壮的,也不是那些最聪明的,而是那些对变化做出快速反应的。——达尔文

开发模式不同,软件架构的设计要求也不同:

◎ 在瀑布模式下,往往会预先设计好系统所需要的一系列要素,进行软件建模、详细的架构设计、文档编写,直至设计的工作完成。在项目实施的过程中,会严格按照这些设计来执行。倘若架构有问题,那便是一个相当严重的事故。

◎ 在敏捷模式下,则是先有架构蓝图,实现对应架构的PoC(概念验证)。然后在实现的过程中,基于PoC产生的代码叠加业务代码。接着,在项目实施的过程中,不断地完善应用架构的设计,如软件建模等。

这便是敏捷模式和瀑布模式的对比,采用敏捷模式是为了应对用户需求的不断变化。而需求的变化,则可能会和早期确认的方向不一致,与最初设计的架构不匹配。互联网应用都是敏捷型应用,需要应对不断变化的用户需求,及时改进和调整架构。

瀑布模式采用的是完全计划式设计,即在编码前,由顶层至底层进行的一系列架构设计,包含了各个模块、服务、函数和接口。而演进式架构,则是预先设计好重要的部分如模块功能划分、领域划分等粗粒度,随后在编码的过程中,再进行函数和接口的设计与实现。

事实上,瀑布模式也无法采用完全的计划式设计,因为无法事先对系统进行全面的了解。而即使是纯粹的敏捷项目,也无法采用完全的演进式架构,还是需要结合计划式设计。以软件开发和设计领域的专家Martin Fowler的观点来看,两者间最合理的分配是20%的计划式设计,80%的演进式设计。这样的设计需要我们注意以下几个方面:

(1)在项目初期进行计划式设计,以确保架构能处理最大的风险。

(2)在迭代0进行初步的架构实践,编写示例的架构性代码——以将设计原则、风格融入项目中。

(3)在项目实施过程中,采用局部设计或演进式设计,以应对需求变化。

(4)配合重构、测试驱动设计与持续集成等敏捷实践,来驱动架构的实施,并防止架构腐烂。

在计划设计阶段,可以制定前后端分离架构的基本规范,如使用JSON作为API的数据格式,使用RESTful作为API的规范。同时,进行核心的技术选型,如选择后台服务的框架、数据库等。对于前端界面的框架,则可以在项目的迭代0里进行更进一步的技术选型。在项目实施的过程中,如果发现工具不合适,也可以在适当的时候尝试使用更好的工具,它需要我们以一种变化的心态来看待架构在项目中的意义。

相似的还有诸如API的设计,我们可以在计划设计阶段进行API的功能定义。而真正的接口定义——返回的字段、详细内容和值类型则属于详细设计,可以在后续实现的过程中加以完善。

1.3.3 持续性

从某种意义上来说,持续性和演进式有些类似。两者的区别是,演进式是指架构上的一些变化,而持续性针对的是开发人员的变化。架构的持续性原则的意图是,敢于修正架构中的错误部分,在修正的过程中尽管可能会带来一些不合适的中转式架构,但是也会很快被纠正过来。具体地,持续性包括以下几个方面:

技能水平的持续改进。随着代码的增多,架构也在不断地变化,也需要不断地被纠正,以符合现有的需求。这就要求团队里的成员既要懂得现有的架构,还要有能力来做一些改变。为此,团队成员需要在项目中进行相应的工程实践。在日常的开发中,使用敏捷方法实践相关的持续集成、持续部署。此外,还要关注与学习相关的技能和实践,以协助我们改善架构。如Neal Ford所说,开发中善于发现抽象与模式,并借助测试驱动开发,利用重构进行导向设计。并使用一些质量改善工具,用它们产生的指标来发现问题。

应用的持续改进。除了业务应用本身,互联网应用还需要一系列的配套服务日志、用户行为日志、性能监控等。如果以互联网公司的思维来看,这些事件都不会是一蹴而就的,“先上线,后解决问题”才是真理。在项目开始阶段,某些部分可以手动来完成。然后,在项目演进的过程中不断开发相应的功能,以满足自己的需求。

如常见的日志功能,开始时可以手动进行相关的查询。而后将其暴露到后台服务应用中,再有针对性地对数据进行展示。多数组织对于这些工具的完善是抱着一种持续改进的态度来进行的。

设计能力的持续提升。如果出于人力、物力所限,设计的架构不够合理,也没关系——人的能力是在不断提升的。若是有机会回到之前的场景,再慢慢思考这个问题,仍然有可能感觉当时的方案是最合适的。因此,对于方案而言,不存在最佳答案。建议大家在设计的时候为未来的改进留下一定的空间,以便于在未来隔离出这部分设计。

值得注意的是,关于架构相关的重要决定,还有一点很重要——延迟决策。如果架构上有多个可演进方向,无法做一个合适的决策,那么可以在条件更加充分的时候再做决策,而不是花费大量的时间盲目地修改架构,那样只会造成资源浪费。