iOS证券交易App组件化方案

一、前言

1.1 什么是组件化

组件:强调物理拆分,以便复用。例如:图片库网络库等。

模块:强调逻辑拆分,以便解耦。例如:订单模块账户模块等。

介于业界习惯称之为组件化,所以我们继续使用这个术语。【组件化 = 功能组件 + 业务模块

1.2 为什么要组件化

  • 解决代码(工程)臃肿问题,提高编译速度。
  • 核心业务模块化,职责分离,提高代码安全性。
  • 通用功能组件化,提高复用率及完整性。
  • 协议编程具体化,形成独立子系统,可灵活配置。
  • 提高模块自治力,独立升级部署,相互间不形成绝对依赖。
  • 支持异构,提升系统技术多元化能力。

二、业界方案

2.1 组件化演进之路

// TODO

2.2 微服务概念

2.2.1 三个⻆色

服务中介:联系服务提供者服务消费者的桥梁。

服务提供者:将自己提供的服务地址注册到服务中介。

服务消费者:从服务中介那里查找自己想要的服务的地址,然后享受这个服务。

2.2.2 三个⻆色在iOS组件化中的体现

iOS客户端是一个单工程设计,简单说就是APP作为一个独立个体,所有的业务生命周期与APP是一致的。并不能真正实现微服务中的“每个服务完全独立生存、管理、销毁”。为此,组件化更多是解决职责分离高复用独立发布 (非独立运行)问题。

iOS中服务提供者通常指的是业务提供方,比如:行情模块需要调用交易模块交易记录接口,此时交易模块即作为服务提供者。而行情模块作为服务需求方,即为服务消费者。由于模块与模块之间可能处于同一级,也可以不同 级。按照《软件工程设计:分层概述》原则,同一级模块间不能相互依赖,也就是不能相互直接调用。此时就需要 一个服务中介来解决模块间通信问题

2.3 组件间通信常⻅方案

2.3.1 基于路由 URL UI ⻚面统跳管理

概念

统跳路由是⻚面解耦的最常⻅方式,大量应用于前端⻚面
通过把一个 URL 与一个⻚面绑定,需要时通过 URL 可以方便的打开相应⻚面。 比如

bebull://quote/stockDetail?stock_id=00700 /**个股报价⻚**/ 

优点

  • 多端一致,H5、iOS、Android、Weex/RN/Cameron 、PC、Mac等。
  • 浏览器、其他APP可以通过openURL直接唤起APP指定⻚面。
  • 可结合服务端下发配置表,动态绑定/解绑指定URL实现⻚面灰度。

缺点

  • 无法支撑较为复杂操作及数据传输模型,比如跨模块⻓调用链场景。
2.3.2 基于反射的远程接口调用封装

概念

此类型适合动态语言,借助反射技术进行动态化调用,比如OCRuntime,可以采用以下方式调用:

Class quoteManager = NSClassFromString(@"QuoteManager"); 
NSArray *stockList = [quoteManager performSelector:@selector(getStockList)];

优点

  • 调用较为方便
  • 代码自动补全
  • 编译检查有效

缺点

  • 存在hardcode问题
  • Swift静态语言不支持Runtime
2.3.3 基于面向协议思想的服务注册方案

概念

参考后端Dubbo服务框架,通过服务注册的方式来实现远程接口调用的。 即每个模块提供自己对外服务的协议声明(Protocol),然后将此声明注册到服务中介。调用方能从服务中介看到 存在哪些服务接口,然后直接调用即可。

@protocol QuoteModuleService
- (NSArray * _Nullable)getStockList;
@end
@interface QuoteModule : NSObject<QuoteModuleService>
@end
@implementation QuoteModule
+ (void)load {
  [ServiceManager registerService:@protocol(QuoteModuleService)
                  withModule:self.class];
}
- (NSArray * _Nullable)getStockList {
  return nil;
}
@end

id<QuoteModuleService> module = [ServiceManager
objByService:@protocol(QuoteModuleService)];
NSArray *stockList = [module getStockList];

优点

  • 调用方便
  • 代码自动补全
  • 无需hardcode,面向协议,减少对接错误⻛险

缺点

  • 高度依赖协议,协议变更时容易导致所有依赖方编译失败。
  • 存在服务注册过程,有一定性能开销。
2.3.4 基于通知的广播方案

概念

直接基于系统的 NSNotificationCenter

优点

  • 实现简单。
  • 适合一对多场景。

缺点

  • 不适合复杂数据传输场景。
  • 同步调用不方便。

三、架构设计思路

3.1 目标

  • 业务模块分离,业务职责单一,划清边界。
  • 业务模块间通信灵活且统一化。
  • 业务模块间通信协议严格标准化,具备准入原则。
  • 通用逻辑独立化,构建稳定后采用二进制形式链接。
  • ⻚面多端统一性,方便后续PAAS化(可由服务端自主下发配置,灵活管理⻚面调用及组合)。
  • 代码安全,只开放必要的模块代码权限。
  • 服务异构化,后续部分模块可以采用更多语言如c++python等开发后引入。
  • 设计模式规范化,明确各场景采用MVCMVVM等,如何组织类关系等。

3.2 组件间通信场景

3.2.1 场景一:UI独立⻚面

采用 路由 URL UI ⻚面统跳管理方案 ,实现跨端 + 端内任何场景可直接唤起指定⻚面能力。

3.2.2 场景二:跨模块UI嵌套

采用 基于面向协议思想的服务注册方案 ,通过 BNBee组件中心UIService 进行服务调用,获得指定的 View、VC。

3.2.3 场景三:跨模块数据传输

采用 基于面向协议思想的服务注册方案 ,通过 BNBee组件中心OpenService 进行服务调用,提供同步返回

(sync)和 异步返回(async)能力。

四、架构分层

4.1 图示

4.2 各层职责

总体分为 业务层公共层基础框架层 三层。

业务层

  • 承担业务层面相关工作
  • 主要包括VCViewModelViewModelDCManagerAPIClient
  • 目前阶段分为行情交易资讯账户公共主体五类

公共层职责

  • 承担整体性非业务相关的相关工作。
  • 主要包括公共相关事务(如BNURLHelper),组件间通信API、Model,以及皮肤资源整合。
  • 目前阶段分为公共基础平台、业务间通信API、皮肤资源中心。

基础框架层职责

  • 包含一些日常使用,趋于稳定的库。
  • 主要分为自研和第三方依赖两大类。
  • 自研部分采用framework静态库方式,基于pod引入。

4.3 各层间依赖关系

  • 同层之间禁止直接依赖,通信方式通过BNBee组件服务中心。提供方提供具体实现,将协议定义于BNUAPI工程中;消费方于BNUAPI中获得对应API及model。
  • 上层可以直接引用下层,也可以跨层下级引用
  • 下层禁止引用上层。

4.4 模块说明

  • 行情模块:包括行情一级tab下的自选列表、市场列表、个股报价⻚等。
  • 交易模块:包括交易一级tab下的开户、资金账户、持仓、买入卖出、交易记录、入金出金等。
  • 资讯模块:包括资讯一级tab下的资讯列表、资讯详情⻚等。
  • 账户模块:包括我的一级tab下的用户登录、注册、个人资料等。
  • 公共基础平台:主要涵盖与业务无关的部分,包括暂时找不到基础框架层可以下沉的暂留组件,比如:WebView、 tabbar等。
  • 业务间通信API:主要负责上层业务模块的组件间通信API协议定义、传输model定义、业务通知key等。 皮肤资源中心:存放所有必牛项目的图片、lottie动画等资源。

4.5 模块内结构

五、BNBee(待补充)

5.1 基本能力

5.2 URL注册绑定

5.3 面向协议服务注册

六、自研框架库

6.1 目标

  • 将重要且需要稳定的模块独立出来,抽离成framework,实现复用性稳定性安全性目标。
  • 标准化一些常用逻辑,做到多业务一致。
  • 代码安全,只开放必要的代码权限。
  • 服务异构化,后续可以采用更多语言如c++python等开发更多底层框架。

6.2 组件库

  • 网络组件库:负责socket⻓连接管理、HTTPS短链接生命周期、连接、安全、可靠性管理。
  • 组件服务中心:负责管理组件间通信,提供基于URL注册、协议绑定等方案管理。
  • 日志组件库:分级日志系统,对infowarningerrorfile及日志收集存取上报管理。
  • 开发组件库:日常开发常用的工具类系统类分类等。
  • 数据上报组件库:直接接驳三方数据上报平台,提供与上报平台无关的通用接口,包括曝光时⻓曝光量统计接口
  • 分享组件库:主要提供通用分享组件,包括分享到QQ微信Twitter等。
  • UI组件库:主要沉淀通用UI组件,如嵌套Scrollviewtabbar等。
  • 数据管理组件库:主要包装内存级缓存磁盘级缓存的接口,后端对接沙盒、SQLite数据库等。
  • 皮肤组件库:主要提供style定义系统,用于约定常用UI组件如UIButtonUIView的多肤管理。
  • 健康防护系统:防范常⻅类型crash兜底,启动连续crash绕行等方案。
  • k线组件库:k线组件作为核心性能指标,沉淀出来方便后续各业务使用。

七、第三方依赖库

7.1 设定准入标准

券商APP的安全性不容小觑,对于第三方依赖库的引入需持有谨慎的态度;因此后期需要增加第三方依赖库的准入 机制,形成选型评审安全鉴定引入的原则。同时,尽量剔除一些功能重复、失去维护、功能滞后的三方库, 保证APP包大小启动性能等各项指标。

7.2 选型需明确

如:选新不选旧 or 稳定大于能力?Swift实现的优先?轻量化还是重量级?是否跨端配套⻬全?等等问题,需要有 明确的标准,组件化工作落地后,需要再明确这块,保证快捷接入的同时,可以更稳定、更一致。