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

1.2 架构的设计

架构设计并非只是一个技术工作,它包含了一系列复杂的工作,其范围包括软件工程、开发实践、业务交付等相关的领域。为此,在进行架构设计的时候,需要进行一系列技术及非技术相关的工作,如图1-1所示。

图1-1

相应的步骤如下:

(1)收集利益相关者的需求。倾听业务人员、项目负责人等相关者的需求,进行用户访谈,收集相关的需求。

(2)与相应的技术人员(如开发人员、测试人员)讨论,了解架构上的潜在限制。

(3)寻找潜在的可行性技术方案。

(4)整理出功能列表中的功能性需求和跨功能性需求。

(5)找出会严重影响开发的风险点。

(6)和技术委员会、利益相关者反复确认方案(可选)。

(7)对架构设计进行概念证明。

(8)细化架构的部分实施细节。

(9)结合技术和业务,进行需求排期。

对于不同项目来说,上述步骤都会有所不同,有的可以直接忽略一些步骤,有的则会包含更多的步骤。在项目实施的过程中,如果有部分需求无法确认,就需要延迟决定。

在这个过程中,最重要的还是收集相关的架构需求。有了这些基本的信息之后,才能进行相关的技术决策。因为对于大部分项目来说,技术方案往往都是现成的。从过往经验、书籍、互联网、同学、同事等渠道,都可以获得一些不错的技术方案。只是每个项目因为需求的不同,所需要的技术方案也略有差异——往往需要整合多个方案,或者对某个方案进行改进,而需求能决定这些架构方案的细节。

1.2.1 收集架构需求

不得不强调的一点是,架构并非完全从技术角度来考虑问题。它需要从多方的利益出发,在满足利益相关者需求的同时,还要具备技术上的可实施性。为了寻找技术相关的因素,我们需要进行相关的讨论,收集一系列的相关资料:

◎ 了解相关者的利益。

◎ 寻找架构关注点。

◎ 明确跨功能需求。

◎ 罗列技术风险点。

有了这一系列的内容才能进一步明确,我们的架构需要解决什么问题。

1.了解相关者的利益

架构设计的目的是解决一个应用的设计与实施,而应用则是为了满足利益相关者的需求而创建的。为此,在构建应用、设计应用架构的时候,需要考虑相关者的利益。这些利益相关者如图1-2所示。

图1-2

他们有各自的关注点:

◎ 产品或业务负责人(PO),关心是否能按时上线。

◎ 项目经理,根据架构来决定项目计划及项目人选。

◎ 架构师、开发人员,关心系统的构建、演进及维护。

◎ 业务分析人员,关心如何分配和安排项目的迭代计划。

◎ 测试人员,设计合理的测试计划,如对系统集成部分的测试等。

还可能存在级别更高的利益相关者,诸如创业公司的投资人,或者公司中的CTO、CIO等级别的人物——对于中大型公司而言,项目会因为战略目的由他们来发起,但不会有足够的精力投入相关的实践上。只是实施的过程中,我们需要听取他们对于项目的期望,以及与之相对应的预期(如战略上的预期)。

因此在设计架构的时候,需要考虑到各方的利益诉求,再坐到一起讨论出折中的方案。由于各方的利益诉求并不会一致,往往需要某一方做出一定的妥协。如业务方需要更快的页面响应速度,但受限于数据量大或者查询复杂,并不能给出对应的速度。那么,作为一种折中方案,可能是用户体验设计师和前端开发人员,设计一些加载动画,来响应用户的行为。

尽管不能一一满足各方的所有诉求,但是满足了部分诉求之后,仍然是一个让人满意的架构——它让不同利益的相关者达成共识。至少在达成一致之后,我们可以继续往下设计系统。

2.寻找架构关注点

在设计架构的过程中,各个组织、开发人员、架构师都拥有自己的关注点。在大部分的项目中,都会拥有相似的一些关注点,也因此它们会来自于其他项目的经验。常见的关注点如表1-1所示。

表1-1

不同的项目因为自身的需求,对于各种特性的优先级考虑往往是不一样的。有的项目认为安全更重要,由安全带来的用户体验下降的问题是可以允许的;而有的项目认为用户体验更重要,也便能忍受其影响到其他特性。

我们在进行相应决策的时候,也需要和相关者进行讨论。如因安全问题导致用户的体验下降,而安全又是基线,则需要告知相应的利益相关者。在反复确认之后,我们便会得到这些关注点的优先级顺序。这个优先级,对于我们后续的工作非常有帮助,可以减少在设计上出现的返工。

3.明确跨功能需求

按功能性来划分,需求可以分为功能性需求和跨功能性需求(又称为非功能性需求)。功能性需求定义了一个软件系统或组件的功能,也是一个系统需提供的功能及服务https://zh.wikipedia.org/wiki/功能需求。而跨功能性需求也是需求的一个重要组成部分,它指的是依靠一些条件判断系统运作的情形或其特性,而不是针对系统特定行为的需求。这些非功能性需求一般是隐性的,往往难以直接观察得出。

因此在设计架构的过程中,我们需要详细列出实现跨功能需求的计划。它们从源头可以分为如下两类。

◎ 运行质量(Execution Qualities):即可以在系统运作时观察到的质量,例如安全性、易用性等。

◎ 演进质量(Evolution Qualities):它们体现在系统的静态结构中,例如软件可测试性、可维护性、可扩展性、可伸缩性(Scalability)等。

这些需求,有一部分是与性能(后端)指标相关的,例如同时支持的并发数,接受的宕机时间等。它们往往用各种“xx性”来描述,举例如下。

◎ 可用性:是指在一段时间内,系统能够正常运行的概率。如何保证这样的可用性?

◎ 可维护性:其表现的指标是,在接到修改后,可以在相应的时间内(如小功能1~3天)修改完成。

◎ 可变性:在未来,应用的哪些地方会发生变化?是否需要提前做好准备?

◎ 容错性:系统中可能会出现哪些故障?如何确保这些故障不会让系统无法运行?

◎ 可伸缩性:当更大规模的用户使用系统时,哪些服务会出现问题?

多数时候,对于后端服务来说,它们只会间接影响架构的决策。但是对于前端应用来说,它们往往会影响应用的架构——部分相关的内容是一些兼容性、跨平台相关的需求,举例如下。

◎ 浏览器的支持范围:明确指出我们要针对哪些浏览器,以及浏览器的哪些版本做兼容。

◎ 移动端设备支持的版本:常见的设备如Android和iOS,设备最低的支持版本,以及支持哪些设备制造商。

例如,我们决定支持早期的IE8浏览器,那么就无法选择流行的前端框架——React、Angular、Vue,便不得不从早期的前端框架(如jQuery、Knockout、Backbone)中选择。从这一点来看,它会严重影响前端系统的架构。如果我们决定支持IE9相关的浏览器,那么还需要花费一些时间来进行相关的适配。此外,在日常的开发中选择一些辅助的开发库时,也不得不考虑这样的因素,因为它也会为测试带来一定的工作量。

同样的问题也存在于客户端应用中,如在开发Android应用时,希望能支持Android 4.4以下的系统,同样也会大大地影响系统的架构。在低版本的Android系统中,缺乏一些系统特性,导致在实现的过程中需要有针对性地调整系统的架构。

注意:其中的部分跨功能性需求可能是由业务人员、产品经理提出来的,他可能对这些问题不是非常了解。对于这一类问题,要坚持以数据来说服人——要么提供不同浏览器、系统的占比,要么提供支持向下兼容的成本估算——真实的估算。

4.罗列技术风险点

在设计架构的过程中,还需要寻找系统中的一系列技术风险点,并努力降低它们带来的风险。因为系统的风险部分,往往才是最影响应用开发的部分——不确定性意味着其带来的风险是未知的。常见的风险类型如下。

◎ 技术风险:如果在应用中某一个功能的实现比较复杂,或者团队缺乏某一领域的经验,如AI,那么在实践的时候可能会影响系统的架构。

◎ 第三方系统集成:在大中型公司内部,这是一个令人头痛的问题。如果集成后端服务,那么只需要确认对方的API能否按时上线,并按照我们的规范编写即可。如果集成客户端的SDK或者组件,那么第三方要以Web或原生代码的形式集成到系统中。不论哪种方式,都需要提前进行技术方面的评审,并在上线前安排联调。

◎ 受限制的线上运行环境:在开发人员和运维人员部门分离的组织里,往往会在内部限制某些语言,如选定Java等语言,限制其他语言的使用。如果选定的是其他语言,如JavaScript+Node.js,那么运维人员便难以维护,需要进行培训或者招聘新的运维人员。

◎ 与此同时,若是一些需求带来额外的技术膨胀,则会为按时完成应用的开发带来风险。比如,想要实现一个复杂的前端动画效果,经过评估可能需要两星期的开发时间,如果实现这个动画效果将无法按时上线。这时,要么努力去说服业务人员修改需求——适当地降低交互效果,要么将相应的功能排期靠后。

当系统中要大量地集成第三方系统时,系统的风险就会变大。对接第三方系统时,会涉及接口不一致,导致两边需要反复修改才能对接API。对于这些系统的对接,我们往往只能估计一个大致的时间,并预留一些调试时间。在调试过程中,可能会出现一些意外,如人员休假、bug不好修复等问题,导致出现一定的延迟,影响应用上线。而当出现大量的第三方系统时,总的延迟可能性就更高,风险也就越大。

1.2.2 架构模式

从计算机发明至今,软件架构从混沌时代的大泥球模式,发展到丰富多彩的多架构模式,已经积累了大量成熟的架构。也产生了一系列架构的开发方法,以及相应的架构模式。在日常的开发中,这些模式和方法都有相应的参考价值。不得不强调的是,这些方法和模式都有各自的场景,并不适合直接套用。

架构风格便是架构模式的展现形式。

1.架构风格

架构风格是描述某一特定应用领域中系统组织方式的惯用模式。

应用的软件架构并非是凭空想象出来的,它们往往是在实践的基础上总结出来的。不同的行业有不同的特性,相关的架构模式也不同。对于某一类型、领域的应用来说,它们在架构上存在一些相似之处,这些相似之处在于组织系统的方式,也就是架构的风格。当然了,在类似的应用和领域会存在相似的架构风格。

在日常的开发中,常见的架构风格有:

◎ 分层风格。这是最常见的架构风格,它将系统按照水平切分的方式分成多个层。一个系统由多层组成,每层由多个模块组成。每一层为上层提供服务,并使用下层提供的功能。最为人所知的分层架构应用是OSI七层模型和TCP/IP五层模型,在开发后端服务的时候得到了广泛的应用。如在采用Spring MVC开发的后端应用中,Controller层在接收后端请求时,将通过Service层向DAO(Data Access Object,数据访问对象)层请求数据,而不是直接向DAO层请求数据。

◎ MVC架构风格。这种风格应用得相当广泛,它强调职责分离,将软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。由视图和控制器一起完成用户界面的功能,并设计一套变更机制,来保证用户界面与模型的一致性。它是一种常见的架构风格,在涉及图形用户界面时,往往都有它的身影,如前端应用、移动端应用等。

◎ 发布-订阅风格。这种风格又可以称为基于事件的架构风格,它是一种一对多的关系,一个对象状态发生改变时,所有订阅它的对象都会得到通知。这种架构风格带来的最大好处是,代码上的解耦。发布者不关心事件的订阅者,只需要在适当的时候发布相应的事件即可;而订阅者则依赖于事件,而非事件的发布者。在前端的日常开发中,为了解耦不同的UI组件的依赖,会经常采用这种模式。

◎ 管理和过滤器。这是一种适合于处理数据流的架构模式,它将每个步骤都封装在一个过滤器组件中,数据通过相邻过滤器之间的管道传输。最典型的管道-过滤器架构是UNIX shell的设计。在类Unix系统中,使用“|”作为管理符号,当我们需要编写复杂的Shell脚本来处理内容时,便会使用这个符号。诸如ls-l|grep.jpg,便会先执行ls-l命令,再将结果交由grep程序,查找以.jpg结尾的文件名。它也适用于相关的数据处理场景,如我们在采用Hadoop、Spark等编写数据处理相关的代码时,便会采用这种模式来编写。如前端框架的Angular,也直接内置了管理(Pipe)系统。

除了上述几种风格,还有诸多的架构风格也比较常见,但是它们偏向于后端架构,与前端架构没有直接的关系,有兴趣的读者可以从本章后面的参考书籍中进行更详细的了解。

和设计模式一样,架构风格可以体现架构的一致性,提高人们对架构的可理解性,可以在新项目上实现重用。在设计架构之前,可以了解一些相关的架构风格,以便更好地设计架构。

1.2.3 架构设计方法

时至今日,我们已经拥有一系列架构设计的方法。这些架构的开发方法和开发流程,也和架构开发的模式有关。但是由于这些设计方法往往是为了解决复杂的问题而存在的,因而对于一般的项目来说,充满大量冗余的步骤,并不很实用。而且,它会使开发人员只关注于工具的使用,而不是设计出合理的架构。

在这里,我们将简单地介绍一下4+1视图法及TOGAF,以配合本章的其他部分来进行架构设计。与此同时,这两种方式,侧重于从系统的角度来考虑问题,若只是从前端的角度来考虑,未免有些大材小用。

1.架构开发方法:4+1视图法

每个项目都会有一张系统架构图,里面包含了系统的主要层级之间的关系,以及一些第三方系统之间的关系。这张图不仅会涉及系统的软件架构部分,还会涉及系统相关的部署内容。

我们可以通过开发、部署、流程相关的内容来了解系统的架构,也可以利用Philippe Kruchten提出的“RUP 4+1视图方法”来进行相应部分的设计和架构探索,如图1-3所示。

图1-3

图1-3中的逻辑视图、流程视图、开发视图、物理视图和场景的含义如下。

◎ 逻辑视图(Logical View):在设计期的模块、接口划分,职责及协作方式等。

◎ 流程视图(Process View):在运行期运行的数据同步,如在微前端中的数据流、控制流等。

◎ 开发视图(Development View):在开发期的框架、库、技术选型及其对应的编译。

◎ 物理视图(Physical View):又称为部署视图。在部署期与持续交付相关的技术决策。

◎ 场景(Scenarios),又称为用例视图,它使用一组用例或场景来说明架构。

从图1-3可以看出,4+1视图法的设计流程如下:

(1)架构人员根据需求创建相应的逻辑架构,开发人员进行详细设计。

(2)架构人员和开发人员根据需求设计物理架构,再由开发人员根据物理架构进行对应的详细设计。

值得注意的是,4+1视图法是倚靠软件建模和OO技术来进行架构设计的,尤其在逻辑架构设计方面更符合传统的瀑布模式,需要事先设计好各类接口,才能进入项目的开发阶段。这种烦琐的设计方式,既不适合于互联网应用,又不适合于前端应用。

尽管如此,这种4+1视图法的展现形式,也是我们需要的形式。即从四个不同的维度(逻辑架构、开发流程、部署架构、运行时)来考虑软件技术的设计。

2.架构开发方法:TOGAF及ADM

针对大型的企业架构来说,可以采用TOGAF(The Open Group Architecture Framework,开放组体系结构框架)的标准化方式来设计企业架构。从1995年发布第一个版本至今,已经发布了9个版本——在笔者写本书的时候,最新版本为9.2版本,该版本将企业架构分为如下4个架构域(Architecture Domains)。

◎ 业务架构(Business Architecture):定义业务战略、治理方法和关键业务流程。

◎ 应用架构(Application Architecture):为将要部署的各个应用程序提供蓝图,并展示它们的交互及与核心业务流程的关系。

◎ 数据架构(Data Architecture):描述了一个组织的逻辑、物理数据资产及数据管理资源的架构。

◎ 技术架构(Technology Architecture):定义了支持部署业务、数据和应用程序服务所需的逻辑软件和硬件功能,它包含了IT基础设施、中间件、网络、通信、处理和标准。

在没有足够的时间和精力的情况下,往往很难建立一个涵盖4个架构域的详尽的架构描述。此外,还有一套架构开发方法叫作ADM,用于创建企业级架构。ADM一共分为8个阶段,如图1-4所示。

图1-4

下面对8个阶段进行解释:

A.架构愿景。设定项目的范围、约束和期望。

B.业务架构。开发业务架构,用于支持设定的架构愿景。

C.信息系统架构。开发信息系统架构,用于支持设定的架构愿景。

D.技术架构。开发技术架构,用于支持设定的架构愿景。

E.机会及解决方案。为前几个阶段定义的架构进行初步实施计划和交付工具的识别。

F.迁移计划。通过最终确定的详细实施和迁移计划,阐述如何从基准体系结构迁移到目标体系结构。

G.实施治理。准备和发布架构契约,并对实施的架构进行监督,以确保实施项目符合架构的要求。

H.架构变更管理。对架构变更进行持续的监控。

每一阶段都有详细的步骤,及相应的产出规范。由于ADM是面向大型企业架构的,其过程和步骤比较复杂。有需要的读者可以阅读国际开放标准组织官方提供的文档(http://www.opengroup.org/togaf),以深入地了解实施方案。

从某种程度上说,TOGAF是面向大型企业设计的架构方法,内容广泛而且过程烦琐。对于中小型企业及中小型应用来说过于复杂,需要按照自己的需求进行一定的裁剪。

3.其他

事实上,现有的架构设计方法针对的都是大中型的后台应用——它们需要大量的领域建模、复杂的部署流程和服务调用关系。上述方式中的多个步骤和细节,对于前端应用而言并不存在。与此同时,在与后台一起设计架构的过程中,便会整合出前端所需要的内容。由于篇幅所限,这里就不展开相关的设计方法的讨论,仅做简单的介绍。

从笔者的经验来看,前端架构可以通过层次设计的方式来进行,即由顶至底进行一层一层的技术决策,再由底至顶逐层验证方案的可行性。关于这部分内容,我们将在本章的最后一节进行详细的介绍。

1.2.4 生成架构产出物

不论采用哪种架构设计方式,都需要留下相应的架构文档,它们将为团队开发打下基础。在进入开发阶段时,作为一个普通的开发人员希望得到的内容如下。

◎ 架构图:它包含了系统的整体架构,用于显示地告诉开发人员,它们是如何构成整个系统的,以及每个部分之间的关系。同时,显式地表明哪些部分是第三方系统,以及它们与该系统之间的关系。

◎ 迭代计划:按照业务和技术的要求,按时间顺序排列出项目的实施计划。由于其中也包含了上线时间,所以也可以从上线时间往前推算出迭代时间。开发流程:定义开发人员的工作方式,诸如采用敏捷还是瀑布的开发模式、何种源码方式(主分支、GitFlow或者Feature Branch(功能分支)工作流),必要时还要提前准备相应的工具和设备。然后,有针对性地对开发流程进行一定程度的裁剪,以真正满足团队的开发。

◎ 技术栈及选型:确定项目中使用的语言、框架、库等相关的技术栈,以及相应的依赖等。

◎ 示例代码:在这些代码中展示架构的风格及相应的设计规范。

◎ 测试策略:明确项目的测试类型、测试流程,以及相应的人员在哪些层级进行测试。

◎ 部署方式:定义应用的部署方式及相应的部署方案。

持续集成方案:描述系统的各个模块之间(如前后端)如何集成,以及采用怎样的时间和频率来集成相关的模块。

有了这些基本流程,开发人员就可以寻找合适的工具,开始搭建项目。反过来,也可以从我们所需要的部分反推出我们所需要的内容。