2022年大前端平台化技术方案

 

背景与目标

平台化是大前端在 22 年的主要技术建设目标,结合目前大前端的技术栈与发展情况,我们制定了如下目标:

  1. 实现大前端通用业务逻辑下沉,解除大前端基础组件和通用业务对公司业务的耦合
  2. 构建以平台化为核心的业务容器平台,并实现快速集成复用方案,至少实现基础组件与核心业务能力的可复用性
  3. 建立全链路的业务性能监控体系,大幅度提高线上问题预警能力、发现能力、定位能力,提高业务稳定性;完成性能评价自动化装置,完善应用性能评价体系,形成业务开发技术指标标准,并应用到 CI/CD 系统流程中;加强的线上问题的修复能力,将崩溃率提升至业界领先水平

针对以上目标,我们设计了一套大前端平台化开放系统,本文将详细阐述该系统技术方案。

技术演进路线

大前端的技术演进路线一般分为四个阶段:组件化、平台化、容器化、无容器化。公司的客户端技术曾经进行过组件化的演进,现如今由于平台化需求,以及客户端自身的技术发展和路线规划要求,我们需要在组件化的基础之上再次迭代技术路线,完成平台化的同时探索容器化的可能性。

什么是组件化

组件化又称模块化,是指将一个大型应用按照功能分离的方式,拆分成多个独立的组件或者模块。每个独立的组件都是一个单独的系统,可以单独维护、升级、替换,也可以依赖别的独立组件。只要组件提供的功能不发生变化,就不会影响其他组件和软件系统的整体功能。 组件化示意图

可以看到组件化的中心思想就是将独立的功能进行拆分,而在拆分粒度上,组件化的约束相对比较松散。一个独立的组件可以是一个包、一个页面、一个控件、一些基础函数功能等。 一般我们拆分组件,需要保证以下几点原则:

  1. 单一性原则(SRP):指组件功能单一,具有明确的左右边界,专注只做一件事。
  2. 抽象化原则(SAP):指组件提供的功能应该具有高度的复用性,对外暴露的接口应当尽量减少变化,而不是直接具象的某种功能实现,将可能发生变化的因子在组件内部进行处理,不要暴露给调用方。
  3. 稳定性原则(SDP):指稳定的组件不可以依赖经常变动的组件,避免功能稳定的组件为了适配功能不稳定的组件而经常适配。如果已经发生了这种依赖,应当将不稳定的部分独立出来单独处理。
  4. 自足性原则(SCP),指组件的功能应当尽可能自给自足,尽量减少对其他组件的横向依赖,如果有横向依赖,需要通过一些手段进行解耦,例如使用 mediator 模式等。

从上面的组件功能定义可以看到,组件是个相对松散的广义概念,其大小取决于封装的功能的大小,而组件之间的关系也仅靠依赖去维持。如果组件的依赖关系比较复杂,则会出现一定程度上的功能耦合现象。 如下所示的组件依赖关系示意图中,组件 2 和组件 5 同时被多个业务组件和基础功能组件直接引用,甚至组件 2 和组件 5、组件 3 和组件 4 之间还存在着循环依赖的情况。一旦这些组件的内部实现和外部接口发生变化,整个 App 就会陷入不稳定的状态,即所谓牵一发而动全身。 组件循环依赖现象 而这种组件循环依赖的现象,需要在平台化时才能彻底解决。公司的客户端技术演进经历过组件化的迭代,但是组件化做的并不彻底,很多基础组件并没有完全剥离掉公司属性,同时业务组件的横向依赖也有部分耦合因为业务依赖的原因没有完全去除,因此这一部分是需要继续深化的,这也是接下来做平台化的基石。

什么是平台化

上面我们说到了,单纯的组件化会组件循环依赖的现象。那如何才能解决这些问题呢?我们可以尝试对它们提供的功能进行分类,进行统一分层划分,增加依赖治理的概念。为了对这些组件在概念上进行更为统一的分类,我们按照四象限分析法,把这些组件按照业务和 UI 分解为 4 个维度,来分析组件可以分为哪几类。 四象限图

可以看出通过这样的四象限法,我们将这些组件分成了四类:

  1. 包含 UI 的业务组件
  2. 不包含业务的 UI 组件
  3. 不包含业务的基础功能组件
  4. 不包含 UI 的基础业务组件

这四类组件从技术视角来看,很天然的会形成四种不同的层级,我们将这四个象限根据依赖关系进行分层排序,就会得到以下结果: 依赖关系拆分

可以看到,平台化与组件化最大的差异在于增加了分层的概念,每一层的功能均基于同层和下层的功能之上,这使得各个组件之间既保持了独立性,同时也具有一定的弹性,在不越界的情况下按照功能划分各司其职。 与组件化更关注组件的独立性相比,平台化的组件规划原则会有更多的要求:

  1. 单向依赖原则:对于上下层依赖来说,只允许上层依赖下层,禁止下层依赖上层。
  2. 横向无藕原则:同层组件之间的依赖关系应当严格控制,只允许通过中间件进行调用
  3. 依赖倒置原则(DIP):如果下层确实需要调用上层组件,需要将下层组件的代码写成依赖倒置的配置注入的模式。

对于公司的平台化战略来说,大前端主要做的即基础业务及以下三层的内容,构建顺序也是自下而上一步一步完成。

什么是容器化

上面我们说的平台化,如果从大前端角度来看,实际上则是这样的情况: alt text 每个平台之间实际并不互通,只是不同的平台之间会在基础业务上提供相同功能的模块接口,但是这些组件的开发维护依旧是分开进行的。而这样的结构在保持实际功能的一致性则需要靠人力长期进行维护,并且开发建设流程都需要在不同平台之间并行完成。 同时由于底层实现的一致性无法保证,哪怕有符合平台规范的标准接口,内部的实现细节的差异将会在某些特定场景暴露问题。如遇到开发新功能、扩展原功能范围、或者修改原定功能提供的特性时,不同平台之间的差异将愈发明显,对于同一个功能的不同平台的实现难度也有所不同。 因此,为了消除平台之间的差异,并实现业务能力的最大复用化,大前端借鉴了后端开发的理念,构建出了容器化这一架构图景。

容器化下的分层结构如图: alt text

组件化与平台化之间的区别,我们可以通过四个方面来看:

  1. 从组件之间的调用依赖规范的角度可以看出容器化相对于平台化,核心区别在于容器化规定了组件之间如何调用,通过限制调用手段,采用收发分离的调用形式,彻底解决了横向依赖之间的耦合关系。
  2. 从分层的角度来看,容器化相对于平台化,定义了不同层级之间的通信规范。例如通过平台能力网关隔离了业务层与平台能力层之间的直接耦合,网关内部使用 RPC 与平台能力进行交互,而业务层访问网关则直接进行调用,避免了直接使用 RPC 调用带来的硬编码扩散问题。
  3. 从组件管理的角度来看,容器化相对于平台化,为每层提供了独立的组件管理方案,由于跨层通信不再是代码层面的依赖调用,因此业务与下层代码之间将没有任何硬性依赖,纵向依赖被彻底隔离。
  4. 从业务开发的角度来看,容器化相对于平台化,抹除了平台之间的差异,使业务开发人员不再关心平台上的具体实现,专心完成业务功能。

通过以上四点我们可以发现,容器化相对于平台化更加突出了业务场景的核心地位,所有的基础能力都是为了业务支撑而服务。每个业务单领出来都是一套完整的单机服务,而下层的平台能力层和基础层又是全业务通用。对于最上层的 APP 来说,可以完全不关心具体的业务内容,只需要进行结构性的组合即可。 通过这样的架构形式,我们可以完成在其他应用里嵌入公司的某项功能,或者新应用使用公司的基础服务进行二次开发构建,同时其他的应用的功能,也可以通过容器包装的形式直接植入公司应用内。

为什么要向着容器化方向演进

我们的愿景是将大前端的开发从平台差异中解放出来,将业务环境抽象,将能力进行标准化,形成统一的容器。然后通过容器去屏蔽平台与端的差异性,使得业务开发人员专注于容器内的业务逻辑的实现,最大化复用已有能力,而不用关注现在的开发环境是 Android、iOS 还是小程序,现在的端是公司、玩伴、还是其他的新应用。 容器和远端达成呈现协议,使得端上的内容具备随时可变化的能力。容器化架构的实现是存在一定前提的,如果业务的发展本身处在一个探索阶段,还有较多可变的因素,是无法形成稳定的能力层的,这时候建设容器化架构反而使得架构偏向复杂。但对于公司业务场景来说,经过多年的沉淀固定,公司业务逐渐形成了一套稳定的业务形态,已经进入到场景细分和在娱乐社交赛道快速尝试迭代新业务模块的阶段。在这样的阶段下,容器化架构才有可实施的前提。 整套大前端的开发流程通过容器化这样的开发平台,实现互通有无。因此基础库之类的底层代码需要尽量通过 c++等跨平台语言来实现,不然上层的平台无关化的感知将无法彻底屏蔽。 但是以目前的开发能力和对于业务动态化部分的实践经验来看,我们暂时还不允许构建完整的容器化系统,因此我们将整套技术演进路线拆分为三个阶段,逐级推进,一步一步的完成整套容器化的建设:

  1. 第一步完成总体业务能力的平台化建设,将多环境之间的接口性差异打破,同时完成基础层的能力合并与平台容器能力容器的初步构建。
  2. 第二步完成业务动态化能力的建设,通过将上层业务开发能力进行合并,推动平台能力层跨端跨平台跨语言的能力构建,为上层业务的通用开发能力提供基础。
  3. 第三步完成宿主应用的壳工程化,将业务层组件全部重构成动态业务容器,完成彻底容器化建设。

今年的目标是基本完成第一步的建设,因此本文后续内容将以第一步为主体进行设计论述。后续的推进方案需要根据第一步的推进进展来确定范围,因此暂且不表。

系统架构

alt text

方案设计

总体介绍

整个技术架构以平台化为核心,通过下沉的基础平台组件形成功能支撑。对于符合开放要求的基础平台组件,建立平台基础组件开放平台进行管理,以打包后的库为介入方式;同时将符合平台化功能的业务,通过平台业务容器进行包装,以 SDK 的形式进行发布。 同时并行开发动态化构建系统与平台监控系统,将动态构建系统融入平台业务中,使得平台开放业务的 UI 展示层基本都可以通过动态构建系统快速实现,同时动态构建本身也可以实现独立业务;而平台监控系统的基础协议则直接接入平台基础容器协议中,作为平台容器的基础设施来运行,通过对平台业务的 AOP 切面完成相对精细的业务状态指标把控。

平台业务容器的模版容器提供平台业务容器化所需的基础协议,如跨容器交互、平台性能监控、业务容器统一管理等功能,具体实现则由业务对模板进行配置化自定义来完成。 最终平台化开放系统会输出两部分内容,一类是基于平台基础组件的组件开放平台,以库的形式进行发布;另一部分则是基于平台业务容器的开放平台,以 SDK 形式进行发布。这两者的核心区别在于基础组件只提供支撑功能,不包含大前端角度的具体业务,业务方理论上可以选择其他三方库进行集成,是通用性的基础功能;而业务容器 SDK 则是包含了具体业务能力,业务方理论上必须通过该 SDK 实现业务功能的快速集成,具有一定的业务定制化属性。 集成形式上业务容器通过容器开放后台进行集成,以 framework/AAR 的形式提供,而基础组件则直接通过 CI/CD 方式进行集成,原则上只提供动态/静态库与头文件。 结构上不同的业务容器将分开提供不同的 framework/AAR,然后容器管理器组件通过容器基础协议对集成的业务容器进行管理,同时提供容器间的调用协议。

因此我们的推进路线为:

  1. 明确平台化的开发方向,识别出需要优先完成的平台能力及其基础组件,以便下一步进行处理。
  2. 开发平台能力框架,规定好不同组件、不同层级之间的通信机制,完善其架构。
  3. 将符合平台化建设方向的基础组件进行提取、完善、重构、修改,使其符合我们对平台化的要求。如果没有对应组件则新建该组件,新建组件时要求考虑其跨平台的能力。
  4. 整理现有的平台业务域范围,梳理出符合平台能力层的业务内容,对其进行改造以符合平台化要求。如果没有对应能力则新建该能力,新建能力时需要按照平台能力框架给出的协议进行约束。
  5. 将平台能力嵌入容器中,并进行对应的测试验证,完成收尾工作。

一、基础跨平台开发语言选型调研

详细内容见 跨平台语言方案调研

二、平台能力框架

详细内容见 平台能力框架设计方案

三、基础层建设

基础组件定义

基础组件的定义是指完全不包含旧公司业务,并具有通用性的、承担了特定责任的、可以独立开发测试的功能模块。对于第三方库/SDK的封装也算是基础组件的一种。同时基础组件也是平台化其他功能的基础支撑,因此这一块是需要先完成的目标。

业务剥离目标规划与处理原则

  1. 完成基础组件业务解藕,使其基础参数配置化,避免内置硬编码
  2. 将剥离出的业务转移至上层业务中,不影响公司现有业务功能
  3. 制定统一集成方案,避免集成方案混乱
  4. 对于多端实现的同类型功能组件提供统一标准的接口能力,形成接口功能层面的基本一致
  5. 按照平台化推进排期,提前布局完成业务容器所需的基础组件的处理,避免阻碍业务容器的开发进度
  6. 原则上只允许输出静态库/动态库,特殊情况如基础UI库等,可以包含资源文件
  7. 由于业务方的依赖管理工具无法确定,因此需要完整的集成文档,文档内必须包含不通过依赖管理工具的直接集成方案

新建基础组件的目标规划与处理原则

  1. 新建基础组件尽量考虑代码跨平台方案,推荐使用Rust作为底层开发语言
  2. 基础组件设计方案需最大限度考虑扩展性与通用性,应以IoC为主要设计原则
  3. 多端接口层方案通过Cargo自动生成,再自行修改为对应平台胶水代码

平台基础组件开放平台

由于基础组件基本都是代码,且原则上不提供具体源码,而我们内部的CI/CD系统在新业务方也不一定会接入,因此需要提供一个基础组件的下载管理平台。 同时鉴于基础组件之间的复杂依赖关系,因此该平台需要内置一套依赖管理系统,而从头开发依赖管理系统是一项庞大而繁杂的工作,因此建议使用开源依赖管理工具进行依赖管理,如iOS使用cocoapods、Android使用maven等。通过构建公共源的方式进行组件发布,同时维护一套公共wiki用于文档发布。 基础组件的底层代码通过Rust的包管理工具Cargo进行维护,

四、平台能力层建设

业务域与业务层级定义

对于业务的整理则是大前端平台化的第二步,这一步处理的作用是明确平台化业务输出的范围,并指明从公司业务中提取通用业务转化为平台业务的实施路线。因此我们需要通过作用范围和功能特性来对平台业务进行定义。 平台化的业务根据技术部定义的能力范围分为公司级别、社交娱乐级别、通用级别这三种,而2022年的平台化目标主要集中在社交娱乐与通用层面上,同时服务端在实施平台化的过程中,不同的业务能力都是相对独立的去提供的。 通过这种能力层级的划分和服务端的实施方案我们可以得知,从结构上我们是希望先处理相对通用的业务能力的,并且不同业务能力之间需要隔离依赖耦合。 并且由于大前端的业务领域与用户行为高度绑定,而用户的直接GUI操作和产品行为难以进行通用性抽象,因此我们对大前端的平台业务的定义是剥离了具体的展示层的业务部分。

目标规划与处理原则

通过以上的能力范围定义和平台开放思路,我们可以得出以下几个结论:

  1. 平台业务需要考虑不同业务域之间的轻耦合关系,只允许通过中间件进行调用,避免业务域污染
  2. 从公司现有业务中提取平台化业务,需要对公司现有业务进行梳理,彻底隔离出展示层与数据层,对于需要提供默认展示页面的业务则单独提供demo
  3. 新增的平台业务从原则上也应该是展示与数据隔离的,这一点需要从开发之初就进行隔离处理

根据以上结论我们就可以推导出目前需要的工作内容:

  1. 对于公司已有的业务的平台化,需要对业务进行业务域的明确划分,即对业务的纵向切割,并设计一套跨域通讯机制保持不同业务域之间的数据交换
  2. 通过对业务域内的业务进行不同业务类型的定义,通过对业务的横向切割分离展示业务(与用户行为直接耦合的展示层)与核心业务(处理业务数据的逻辑层),并设计一套展示业务与核心业务之间通讯机制
  3. 目前的平台化范围仅限核心业务层内容,并需要以SDK的形式开放
  4. 需要提供一套SDK开放平台用于集成

参考资料