1.5 前端架构设计:层次设计
从笔者的角度来看,架构设计本身是分层级的,面向不同级别的人时所展示的内容也是不一样的。如我们作为一个架构师、技术人员,在面对同一级别、更高一级别的架构师、技术人员时,说的便是形而上学的东西,如微前端、前后端分离等各种概念。这些概念,对于接触过相关知识的程序员而言很容易理解。而当我们面对经验略微丰富的程序员的时候,说的可就不是:去实现微前端这样的东西,而是需要落实到怎样去做这样的一件事。
不同阶段构成架构的因素是不同的,基于这个思路,架构设计可以分为四个层级,如图1-5所示。
图1-5
相应的层级解释如下:
◎ 系统级,即应用在整个系统内的关系,如与后台服务如何通信,与第三方系统如何集成。
◎ 应用级,即应用外部的整体架构,如多个应用之间如何共享组件、如何通信等。
◎ 模块级,即应用内部的模块架构,如代码的模块化、数据和状态的管理等。
◎ 代码级,即从基础设施来保障架构实施。
在设计的时候,既要用自上而下的方式来设计架构,又要用自下而上的方式来完善架构。从演进式设计的角度来看,我们需要在前期设计的时候,对所有系统级架构及部分应用级架构进行技术决策,而其余部分的架构则可以在实施的过程中考虑。
1.5.1 系统内架构
在设计前端架构的时候,首先考虑的是应用在整个系统中的位置——它与系统中的其他子系统是怎样的。这种关系包含了架构和业务上的关系,以及它们之间的协作机制。对于前端应用来说,这一部分的子系统包含了如下两方面内容:
◎ 其他前端应用。关注如何与这些应用进行交互、通信等。
◎ 对接的后台服务。关注如何与后台服务进行通信,诸如权限、授权、API管理等。
如果是系统间的数据通信(如与后台服务之间的交互),那么只需要规定数据通信、数据传递的机制即可。这一类的通信机制,不仅包含了前后端分离架构的API设计,还包含了各类的数据传递,如OAuth跳转的Token验证等。此外,对于传统的多页面应用来说,也需要关注其中的数据传递,如将Cookie作为用户凭据等。
因此,对于前端与后端的关系,我们所要考虑的主要因素是前后端分离架构的设计。
前后端分离架构其实是一个笼统的概念,它是指前后端分离如何实施的技术决策。它包含了一系列的决策、用户鉴权、API接口管理与设计、API(契约)文档管理、Mock Server使用、BFF(服务于前端的后端)、是否需要服务端渲染等。与此同时,当我们开发应用的时候,所涉及的并不只是我们所在团队的工作,往往还需要与其他团队,或者团队内的其他成员的沟通,才能与后端一起设计出整个前后端分离的方案。
当在一个系统内时,微前端是一个应用间的架构方案,当在多个应用间时,微前端则是一种系统间的架构方案。
微前端是将多个前端应用以某种形式结合到一起,当系统中存在多个前端应用(或者单个前端应用的模块较大)时,就需要考虑使用微前端的方式来拆分。此外,还需要做一系列的技术决策来支持应用的整合。
然后,我们还需要考虑前端的客户端展现形式。在大前端的背景下,它可能是PC Web应用、移动Web应用、混合移动应用(结合Cordova构架)、混合桌面应用(结合Electron框架)、原生移动应用(结合React Native)等,具体选择哪一种技术,取决于我们先前调查的利益相关者的需求。当我们做完上述三个核心的架构决策之后,就需要考虑应用的部署架构了。有的客户端形式可能需要服务端渲染,会在某种程度上影响到前端应用的部署,但是总的影响并不大,往往只需要通过反向代理的配置就可以完成部署的配置。如果与后台服务不在一个域,则需要考虑支持跨域请求,或者让后台做一层代码。
有了这些基本的架构设定,便可以继续设计下一层级的应用架构。
1.5.2 应用级架构
应用级架构指的是,单个应用与外部应用的关系,如微服务架构下的多个应用的协作。它可以是一个团队下的多个前端应用,也可以是一个组织内的前端应用。其在组织中所处的位置,也进一步决定了我们所需要设计的架构方案。
若是从相关的定义来看,它与系统级应用存在一定的交集。但是,笔者将其视为系统级架构的进一步细化。比如,在系统架构的层级里,我们定义了微前端架构,而具体的实施细则则会放在各个应用中实现。至于应用间的数据如何交换,不同的应用有不同的实现方式,通常是通过在相应的层级里定义相应的接口来实现。
由于各应用之间需要通过复用代码、共享组件库、统一设计等减少工作量,因此,我们要考虑以下几方面内容。
脚手架。作为一个基础的模块应用,脚手架用于快速生成、搭建前端应用。它除了包含一个前端项目所需要的要素,还包含着与组织内部相关的规范和模式,如部署模板、构建系统等。团队内的多个应用之间往往会使用同一套构建系统,该构建系统会包含在脚手架中。除了包含一系列的构建脚本,它还可以包含编写好的统一的CLI工具。比如在笔者曾经历的混合应用开发的项目里,就曾经通过编写CLI工具支持多个应用的开发。
模式库。它是一系列可复用代码的合集,如前端组件、通用的工具函数等。其作用是在多个应用之间共享代码,降低修改成本。在设计架构的时候,如果考虑内建相应的UI组件库,就需要考虑结合装饰器模式,将模式库作为一层代理来封装外部的API,以降低后期的修改成本。模式库还包含了用于多个前端应用通信的数据通信库。
设计系统。它相当于更高级别的UI组件库,在这个层级里,我们关注抽象通用的UI模式,用于在多个系统之间共享设计。与模式库/组件库不同,设计系统偏向于设计人员的模式,而非开发人员的视角。
在应用级架构中,我们将决定选择哪种前端框架,并进行相关的技术选型。
1.5.3 模块级架构
模块级架构是深入单个应用内部、更细致地设计应用内部的架构,它所涉及的内容是我们在日常开发中经常接触的。我们所要做的是,制定一些规范或者更细致的架构设计。这部分内容会在我们开始业务编码之前进行设计。在敏捷软件开发中,它被称为迭代0/Sprint 0/Iteration 0,其相关的内容有以下两个方面:
◎ 模块化。它包含了CSS、JavaScript、HTML/模板的模块化。对于JavaScript或者模板而言,其模块化的设计受框架的影响比较深。对于CSS来说,我们也需要设计一个合理的方式来进行管理,既需要考虑全局样式以用于样式复用、局部样式以用于隔离变化、通用变量以方便修改,又需要考虑相应的工具来辅助设计。此外,还需要定义相应的CSS、JavaScript、模块的代码组织方式。
◎ 组件化。它主要考虑的是,在应用内如何对组件进行封闭,以及相应的原则和粒度。
此外,对于不同的框架,还涉及一些框架特定的模式与架构设计,它们会在一定程度上影响单个应用的架构。对于不同的框架来说,所涉及的范围有所不同。如在Angular框架中,不需要关心相关的模式,只需要掌握框架定义的规范即可,如使用Service来保存应用的状态、使用Pipe来处理数据等。而在React框架中,则需要设计状态和数据流的管理方式,即需要诸如Flux或者Redux这样的状态管理方案。
1.5.4 代码级:规范与原则
当我们真正编码的时候,考虑的架构因素是更低层级的内容,这部分架构设计被称为代码级的架构设计,它关注于实践过程中的相关规范和原则。这部分内容相当多并且烦琐,包含但不限于下述内容:
开发流程。它包含了开发一个功能所需要的完整流程——从源码管理方式、代码合并方式、代码提交信息规范、代码规范自动化,到测试编写等一系列的过程。编写开发流程的目的是,保证编写、创建出来的代码能够符合项目的要求。
代码质量及改善。在实施过程中不仅要注重代码整洁,还要注重TDD(测试驱动开发)等相关的实践,并且遵守SOLID原则,以保证代码的质量。此外,还需要制定代码的测试策略,测试的目的并非减少bug,而是用测试来保证现有的功能是正确的。
规范而非默契。在整个架构中,我们会更关注规范化。小的团队可以依赖于默契,大的团队则需要规范。它需要我们关注几个方面:代码风格的统一,如统一化编辑器、IDE等的配置、使用几个空格;代码的命名;如何保持一致性等。
此外,在日常的开发中,还需要注重代码的可维护性——简单的代码更容易被读懂和维护。笔者在维护一个Scala项目的过程中,就深有体会——越是写得抽象的代码,越难以维护。诸如函数式编程是一个好东西,但是好的东西也容易被滥用,导致人们不喜欢这个东西。