跃迁引擎

空気を読んだ雨降らないでよ

iOS Research & Development


洋葱学园 iOS 端组件化重构之路[二]-实施方案

背景

基于洋葱学园 iOS 端组件化重构之路[一]-现状梳理 得出的结论与方案,需要验证方案的可行性及实施成本,设计完整架构图和演示工程,包括后续持续集成的改造思路等。

实施目标

  • 中间件的方案,产出完整调度能力的中间件Demo,包含下沉依赖关系演示
  • 列出现有功能的基于中间件改造成本
  • 如何梳理职能范围,拆分力度如何确认(依赖链是否保持单一)
  • 设计完整架构图,确保双端都可基于组件的独立运行单元测试、运行、迭代
  • Jenkins 基于独立工程改造(能设置指定工程路径)以分支切换的形式

调度演示

具体效果见实际 Demo

演示流程

流程1: C 端用户进入备考

单元测试 APP 启动 -> (首次)中间件调度 C 端用户登录组件进行登录 -> 登录组件完成,通过中间件调度用户数据组件进行 C 端用户数据缓存 ->进入单元测试 APP 首页 -> 点击首页备考入口,通过中间件调度用户数据组件读取已缓存的 C 端用户数据 -> 通过中间件调度备考业务组件

流程2: B 端用户进入备考

单元测试 APP 启动 -> (首次)中间件调度 B 端用户登录组件进行登录 -> 登录组件完成,通过中间件调度用户数据组件进行 B 端用户数据缓存 ->进入单元测试 APP 首页 -> 点击首页备考入口,通过中间件调度用户数据组件读取已缓存的 B 端用户数据 -> 通过中间件调度备考业务组件

流程3: C 端用户进入抖葱

单元测试 APP 启动 -> (首次)中间件调度 C 端用户登录组件进行登录 -> 登录组件完成,通过中间件调度用户数据组件进行 C 端用户数据缓存 ->进入单元测试 APP 首页 -> 点击首页备考入口,通过中间件调度用户数据组件读取已缓存的 C 端用户数据 -> 通过中间件调度抖葱业务组件

中间件设计

YCMediator

基于维护成本解耦力度扩展性现有工程侵入性 的综合评估,决定采取 Runtime 方案实现中间件,并在业内知名中间件 CTMediator 基础上进行强化改造,后续自行维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <Foundation/Foundation.h>

extern NSString * _Nonnull const kCTMediatorParamsKeySwiftTargetModuleName;

@interface CTMediator : NSObject

+ (instancetype _Nonnull)sharedInstance;

// 远程App调用入口
- (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion;
// 本地组件调用入口
- (id _Nullable )performTarget:(NSString * _Nullable)targetName action:(NSString * _Nullable)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget;
- (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName;

@end

// 简化调用单例的函数
CTMediator* _Nonnull CT(void);

改造成本

按照现有工程的组件化现状,将改造成本分为解决依赖链问题的「解耦成本」 ,和填补目前没有中间件调度能力的「通信改造成本」

解耦成本

基本评估

复杂度:★★☆☆☆

挪动代码并没有什么复杂度,复杂度基本体现在需要职能拆分时,如何拆分、合并和重组。

人力成本:★★★☆☆

涉及的实际库比较多,需要实际过一遍代码,且属于前置项,要尽短批量的完成,所以需要完成会花费较多人力的时间成本。

涉及范围

  • 全部中台库
  • 中学基础库(不含业务库)

解耦内容

按照后文梳理出的职能范围规范,拆分代码,各司其职,该下沉的下沉至基础服务中,该上提的提入到通用服务或业务中。

⚠️⚠️非必要情况,只挪动代码,不调整代码逻辑,建议整类迁移⚠️⚠️

如果类中掺杂了不属于该库只能的代码,则继续秉持职能范围规范,并入其他现有库或与其他同职能散落代码合并独立成库。

通信改造成本

基本评估

复杂度:★☆☆☆☆

仅需要在目标库中建立一个Target类,和一个基于中间件建立目标库的 Category 库,共同负责对外通信,无侵入,不涉及旧代码已有逻辑,考虑到组件对外调度接口都不多,所以复杂度很低。

人力成本:★☆☆☆☆

因为不涉及业务入侵,所以引入单元测试时再进行开发即可(只增加通信代码),不会占用多少人力成本。

涉及范围

  • 全部中台库
  • 全部中学库
  • 全部教师/入校库

改造内容

在需要中间件调度通信(同层横向依赖)时,在需要被调度的库当中建立一个Target类,再建立一个与被调度的库相对应的,基于中间件的 Category 库即可。需要横向依赖才进行改造,无横向依赖时不需要改造,理论上,基础服务库之间不应存在横向依赖(具体见架构图)。

梳理职能范围

职能按照基础服务通用服务基础业务通用业务独立业务来划分

⚠️注意:非洋葱自行维护的第三方库不在职能范围内,因为第三方库不会主动与洋葱内部职能库产生依赖关系,只存在单向被引用的依赖关系。

基础服务

基础服务属于能力基建,不与任何具体业务、具体功能挂钩,只提供应用程序的最基本支撑能力,应当放之四海皆通用,是组成应用程序的充分必要条件,由现有中台继成维护。

基础服务应足够 “干净”,不应存在任何形式的彼此横向依赖,如果产生了就需要抽离拆分。

⚠️基础服务原则上不直接提供给业务方使用,由上层通用服务封装后向上层业务提供能力,其目的是保证基建不依赖某一特定技术项目,可随时迁移、替换。

网络

  • 基础网络库

数据库

  • 基础数据库

日志

  • 基础日志库

APM

  • 性能监控
  • 崩溃防护

通用服务

通用服务属于依赖于基础服务之上的,面向业务层的通用服务,同样是提供能力而不是具体业务,不关心具体业务细节,面向洋葱所有业务线提供通用服务能力,是组成应用程序的充分不必要条件,但在洋葱内部同样属于充分必要条件,可供洋葱所有 APP 使用的能力,由现有中台继承维护。

通用服务之间可由中间件进行横向调度,只单向依赖于基础服务

环境/配置

  • 通用宏/静态变量配置(不含业务字段)
  • 热更新
  • 函数扩展/类扩展
  • 环境校验

视频播放器

  • 基础播放器(不含插件、不含特定业务功能)

支付

  • 基础 IAP 内购
  • 第三方支付

分享

  • 通用分享

浏览器

  • 基础通用浏览器
  • 通信桥(Riki)

埋点

  • 数仓日志上报
  • 阿里云日志上报

数据缓存

  • 视频下载缓存
  • 偏好管理

基础业务

基础业务面向业务,向下依赖通用服务,属于面向洋葱内部所有 APP 的通用性的基础业务组件的集合。基础业务兼具能力与功能,可通过配置兼容不同具体业务线的具体业务要求。是组成应用程序的不充分不必要条件,在洋葱内部属于不充分必要条件

⚠️基础业务必须是具有可根据参数动态配置的能力,各业务线开箱即用的状态,或拥有极佳的扩展性帮助业务线快速扩展所需能力

⚠️基础业务只向下依赖通用服务,不依赖其他业务内容

业务播放器

  • 基础功能播放器插件

支付中心

  • 基于通用业务封装的支付功能

分享中心

  • 基于通用业务封装的分享功能

鉴权

  • 通用鉴权

用户数据

  • 基础用户模型

登录

  • 基础登录功能(可配置的不包含具体业务线的登录业务能力,不含UI)

动态化

  • DSL

通用业务

通用业务主要包含可复用的细颗粒度业务功能,非完整业务功能,主要由具体业务线内部负责维护,是该业务线内部可重复使用的功能。

⚠️原则上同样只向下依赖基础业务,不横向依赖其他业务

业务播放器

  • 学习播放器
  • 短视频播放器
  • 简单播放器

用户数据

  • 各端基于基础用户模型扩展的本业务线具体用户模型

登录

  • 基于基础登录扩展的本业务线具体登录功能

客服中心

  • 通用客服中心

鉴权

  • 基于基础鉴权扩展的本业务线的通用鉴权

浏览器

  • 基于基础浏览器扩展的本业务线浏览器

独立业务

顾名思义,基于通用业务构建而成的纯业务组件,向下雨来通用业务,横向由中间件调度进行通信。有个具体业务线负责维护,无需考虑复用性,只做组件下沉

具体业务

  • 业务线登录组件
  • 备考组件
  • 抖葱组件

完整架构设计

持续集成

只需要在 Jenkins 上维护一个单元测试工程即可,用企业包维护测试环境和线上后门环境即可,通过 branch 来区分具体的测试单元。

单元测试工程

  • 集成配置:包含中学、教师和校园的 request host 等基础配置即可

Jenkins

  • 维护 UnitTest_Test 和 UnitTest_Release 两个 Job
  • 每次独立的单元测试用独立的 branch 进行区分,master 分支只保留包含了集成配置和中间件的空主工程,各组件由各 branch 独立添加
最近的文章

找出最长的神奇数列

问题问题描述小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0 和 1 组成的正整数序列,长度为 n。这个序列中的 1 和 0 交替出现,且至少由 3 个连续的 0 和 1 组成的部分数列称为「神奇数列」。例如,10101 是一个神奇数列,而 1011 不是。现在,小F想知道在 …

, , 开始阅读
更早的文章

洋葱学园 iOS 端组件化重构之路[一]-现状梳理

背景当前,洋葱学园 iOS 端工程的组件化水平过低,在影响工程师开发效率的同时,又难以兜住持续集成的影响范围,不利于整体工程的高质量建设,已无法满足日益增长的工程预期与精细化控制的需求。 现存问题如下 缺乏组件必要的独立运作能力 缺乏统一中间件进行调度 无法进行单元测试,回归测试成本高 组件间 …

, , 开始阅读
comments powered by Disqus