作者简介
任跃华, 携程机票前台软件工程师,从事机票android、react和react native技术栈相关研发工作。
前言
携程机票前台团队在使用 React Native 实现众多业务的过程中,经历了前期少量探索,中期大量应用,后期架构和性能优化的三个阶段。
在该技术栈积累了一定经验之后,结合不同业务的特点和复杂性,我们重新审视和思考一些前期实践项目的整体优化方向。在 App 国际机票查询列表页的相关业务模块,基于 Clean Architecture 整洁架构之道的思想,进行了一次技术大重构。
一、GUI 架构回顾
GUI 架构模式,一般分为两类:MV* 和 Unidirectional 。
最初的 MVC 将模块划分为展示界面的 View,数据模型 Model 和负责处理二者关系的 Controller 。从 MVC 到 MVP 的过程将 Model 和 View 完全隔离。随着 Databinding 技术的引入,MVP 进化到了 MVVM,使得 View 完全无状态化。
Unidirectional 系列相较于 MV*,则采用了消息队列式的数据流驱动的架构,其中具有代表性的 Redux 采用了统一的状态管理,带来了状态的有序性和可回溯性。
MV* 系列在 iOS、 Android 生态圈中已得到成熟广泛的应用,而在 React 技术栈的 Web 前端领域, Redux 是最主流的数据管理方案。
不同平台选择不同,这其中有框架 API 设计的原因,有编程语言的原因,以及面对的业务逻辑复杂度不同。React Native 是 React 和 Native 的混合体,原有的 Native 框架 API 被映射成 React Component 生命周期,编程语言也发生了变化,不变的是业务场景和逻辑复杂度。
Redux 曾是我们大型 RN 项目的标配,不过实践结果表明, Redux 的一些固有设计并不能很好的应对复杂的应用场景。因此,我们选择了相较于MV*系列,又对Presenter/Controller做了进一步拆分的Clean Architecture。
二、Clean Architecture
Clean Architecture (附录1)是 Uncle Bob 在2012年提出的用于构建可扩展、可测试软件系统的概要原则。这些架构产生的系统特点是:
框架无关性 - 框架只是一个工具,系统不与框架绑定
可被测试 - 业务逻辑与UI、数据库等隔离,方便单元测试
UI 无关性 - 不需要修改系统的其它部分,就可以变更 UI,如将 React 替换为Vue
数据库无关性 - 业务逻辑与数据库之间需要进行解耦
外部机构(agency)无关性 - 系统的业务逻辑,不需要知道其它外部接口,诸如安全、调度、代理等
基于以上原则的系统架构如下图所示,又称洋葱图。
从外到内,分为四层:
Frameworks & Drivers - 由框架和工具组成,比如各种前端框架,数据库访问工具等。
Interface Adapters - 作用是转换数据,连接内层与外层
Application Business Rules - 将多个业务实体封装为高级具体的业务用例
Enterprise Business Rules - 单个业务实体,可以是具有方法的对象,也可以是一组数据结构和函数
不同层代表软件系统中不同领域,外层是机制(mechanisms),内层是策略(policies)。
层与层之间遵循一个依赖关系原则:外层指向内层,机制指向策略。内层中的任何东西都不能知道外层中的某些东西。特别是外层中声明的内容的名称不得被内层中的代码提及,包括功能、类、变量或任何其他命名的软件实体。出于同样的原因,外层中使用的数据格式不应该被内层使用,特别是当这些格式是由外层中的框架生成时。外圈中的任何东西不应该影响内圈。
2.1 业务场景
App 国际机票查询预订流程中,列表页负责展示符合用户搜索条件的航班列表,并将用户带入中间页(舱位选择),其业务场景有以下特点:
代码量庞大 - 逻辑层70000行以上
依赖服务多 - 依赖11个服务
交互复杂 - 筛选、排序、切换日期、低价订阅和查看浮层等
展示信息多 - 航班信息、通知公告,推荐航班等
页面结构变化 - 单程、往返、多程页面结构不同;不同 ABTesting 页面结构不同
2.2 应用结构
如下图,项目最外层分为公共库和业务两部分。
公共库 - 封装了全局可用的公共代码,如与 Native 通信,发起网络请求和其他通用工具类
业务部分 - 具体的业务逻辑,由多个同构的业务模块嵌套组合而成,分形结构
业务部分由多个Clean Architecture模块组成,最外层模块处理页面路由和页面初始化数据