作者:Sherwin.Chen

一、背景

在移动使用开展进程中,跟着团队人员的扩大、事务杂乱化,代码量随之增多,然后带来了团队协作开发中各式各样的问题:

  • 功用代码之间的依靠杂乱,可保护性差。
  • 协同开发进程中,并行开发存在相互依靠的状况。
  • 功用边界不明晰,根底模块变动,会导致上层事务受到影响。

一个合理的架构规划能够处理大型项目跨团队协作分工和多事务线并行开发的功率问题,货运iOS用户端当时处理这样一个低功率的问题上,需求树立一个技能专项,处理老的架构带来的问题。 终究到达降本增效,提高多人协作的开发功率。

当时货拉拉货运iOS用户端在开发中存在如下三大痛点:

  • [Pod组件版别办理问题]

    • pod组件版别依靠联系办理混乱:有的pod库版别确定在podspec里,有的在podfile里。如遇晋级某个pod组件,需求改N多个当地。在日常的开发进程中经常性因pod版别依靠联系不一致引起pod install/update 失利,需求各搭档排查问题,影响事务迭代的开发功率。
  • [组件路由问题]

    • 当时用户端的模块路由计划为依据Target-Action办法的CTMediator,此计划有如下几个问题:
  1. 调用组件名、办法标识采用字符串传入办法,散落到事务代码的各处,不便于保护。
  2. 组件入口一致运用会集路由接口,经过运用整数标识不同的组件办法名,不便于问题的定位与查找。 事务同学在调用途,无法了解当时调用的数字所代表的事务逻辑。
  3. 路由组件没有对反常进行防护和线上监控,无法监控到模块组件调用的线上运转时状况。
  • [多库房代码提交问题]

    • 货运iOS用户端有四大库房,Huolala/User/Move/CommonModule,各库房内的功用模块区分不清晰,事务功用与根底功用混乱,导致某个需求迭代开发,会在3个或4个库房拉分支开发/提交/MR,在不具备完善的DevOps东西链的状况下,极端的影响开发的功率。

为处理上述问题,由此咱们拟定了如下优化方针:

  • 方针一:提高研制功率, 包含:事务功用代码开发、编译打包测验
  • 方针二:提高项目结构安稳性和可扩展性,削减线上事故发生的概率
  • 方针三: 处理现在开发中的三大问题

二、相关的预研

  1. iOS货运用户端工程架构现状梳理

从工程架构动身,咱们梳理了现在阶段的架构,如下图所示:

货拉拉货运iOS用户端架构优化实践

  • 从联系图能够看出,现行的架构很不合理,除了与根底库之间隔离,各事务之间相互依靠,耦合十分严峻
  • 事务模块中包含了东西组件,并未下沉到公共模块中,重复性的东西代码冗余在各事务代码中。
  • 公共模块中包含了部分事务相关的模块,未上浮到独立的事务层中,导致上下级相互依靠、调用,破坏了安稳依靠准则
  1. iOS业界构架演进方针

  • 组件化为根底的架构方针
  • 接口与服务别离,对事务分层
  • 单仓多组件,对库房结构改进

2.1 组件化完结准则

2.1.1 、笼统化准则

所谓”笼统化”,就是指从具体问题中,提取出具有共性的办法,再运用通用的处理办法加以处理。越底层的模块,应该越安稳,越笼统,越具有高复用度。

2.1.2、安稳依靠准则

不同组件之间的依靠联系必需求指向更安稳的方向。任何一个咱们预期会经常改变的组件都不应该被一个难于修正的组件所依靠,不然这个多变的组件也将会变得十分难以被修正。即不要让安稳的模块依靠不安稳的模块, 尽可能的削减不安稳库的依靠。

2.1.3、事务模块真实解耦

组件化并不是说你把工程的代码拆分红多少个pod就算完事了,要完结模块之间真实的解耦才算真实的组件化。假如模块之间仍是经过头文件引进相互调用代码,循环依靠,那么和原本在一个大工程里用文件夹办理没有差异。要做到真实解耦,需求深入到模块代码里,抽丝剥茧的别离和整合。

2.2 组件化辅导方针

2.2.1、组件化第一步,剥离产品公共库和根底库

  • 对根底才能,比方网络请求、UI组件、设备才能、WebView这些,完全的剥离到根底才能层,进行独立迭代,以二方库办法进行开发与发布。
  • 针对三方库,最好再封装一层(适配层),使事务项目不直接依靠三方库,便利后续功用迭代或新计划的随时改变,然后削减对主事务的影响。

2.2.2、组件化第二步,独立事务模块独自成库

  • 拆分粒度能够先粗后细,将相对独立的组件拆分出来。
  • 在开发进程中,对一些独立的模块,如:登录模块、账户模块等等,也能够封装成组件,因为这些组件是项目强依靠的,调用的频次比较多。
  • 别的,在拆分组件化的进程中,拆分的粒度要适宜,尽量做到组件的独立性。
  • 同时,组件化是一个渐进的进程,不行能把一个完整的工程一会儿全部组件化,要分步进行,经过不停的迭代,来终究完结项目的组件化。

2.2.3、组件化第三步,对外服务最小化

在前两步都完结的状况下,咱们能够依据组件被调用的需求来笼统出组件对外的最小化接口。

2.2.4、可独立的东西或二方库运用pod 私有源办理

  • 可借助公司CICD 打包的才能,为咱们的端上APP打包、发布供给便利性。
  • 将用户端沉淀的二方以及事务端共治的公共二方库,运用打包渠道发布才能进行办理。

三、计划规划与拟定

  1. 方针拆解拟定

货拉拉货运iOS用户端架构优化实践

第一步、组件库依靠办理规划与施行,处理pod组件版别办理问题

第二步、组件路由器计划的规划与施行,处理现行路由器计划低功率不标准问题

第三步、组件化架构分层,处理组件未区分清晰层次,相互依靠网状问题。

第四步、单仓多组件计划规划与施行,将事务模块上浮,东西模块下沉。

  1. Pod组件依靠办理计划

货拉拉货运iOS用户端架构优化实践

  • 创立一个新的pod组件PodDepeLock,每个子组件别离办理几个大组件库的依靠pod的具体版别号
  • 每个大的组件Example下的podfile依靠PodDepeLock下的对应子组件。
  • 一切货运项目中二方库间的依靠不确定版别,由PodDepeLock一致处理。
  1. 组件路由计划规划

3.1、计划规划方针

  • 组件调用接口文档化、可视化,便利运用和保护。
  • 保持组件化解的偶特性,调用方无所依靠组件名,只面向组件接口编程。
  • 独立的组件可独自编译开发,保持最小化的事务开发体验。

对于组件化计划的架构规划好坏直接影响到架构系统能否久远地支持未来事务的开展,对App的组件化不只是只是的拆代码和跨事务调页面,还要考虑杂乱和十分规事务参数参加的调度,非页面的跨组件功用调度,组件调度安全保障,组件间解耦,新旧事务的调用接口修正等问题。模块化解耦需求的更精确的描绘应该是“如安在确保开发质量和功率的前提下做到无代码依靠的跨模块通信”。

PS:当时所做的路由计划是为后续的组件化事务拆分做好铺垫。

3.2、SHSpringRouter计划规划思想

依据protocol-class办法下的路由组件。 包含三大模块:

  • 中心层包含路由注册、服务完结创立。
  • 安全协议接口调用,用于保护未完结的协议办法调用
  • 运转时的路由日志输出,包含:日志、监控、反常库房。

依据Spring结构Service的理念,经过protocol-class注册绑定联系来完结模块间的API解耦。

  • 各个组件以服务的办法存在,每个都可独立,以到达相互解耦的目的。
  • 各个服务具体完结与接口调用别离。 处理网状依靠的问题。
  • 面向协议编程。 即服务与服务调用都是依据服务协议进行合作,经过SHSpringRouter路由到达调用目的。

3.3、SHSpringRouter路由器架构规划

货拉拉货运iOS用户端架构优化实践

  • SHSpringRoute:中心路由处理中台,保护整个protocol-class的联系表
  • SHSafetyService:组件服务完结基类,用于处理办法调用反常的问题。比方未完结某个协议的办法,却被调用了。每个组件服务完结都可承继此类。
  • SHSpringReportLog:当时路由东西在运转时的内部日志输出,用于调试剖析以及线上运转状况收集。

3.3.1 中心流程时序图

货拉拉货运iOS用户端架构优化实践

3.3.2 工程组件化施行架构图

货拉拉货运iOS用户端架构优化实践

  1. 路由接口层树立为独立库房:做为一个组件调用中台调集,每个API声明为其它组件调用的接口,内部的完结经过protocol-class办法进行调用,完全隔离了对其它组件的依靠。
  2. 组件间的调用不依靠服务完结层,经过Protocol进行黑盒调用,完全解耦。
  3. 事务线下的每条事务线调集,可有一个或多个子分类组件完结,进行细化办理。

3.4 组件路由逐渐替换计划

货拉拉货运iOS用户端架构优化实践

  • 第一步,承认好改造方针,分为两个方向:组件接口改造,事务方调用改造
  • 第二步,对已有的组件老路由进行改造,创立一个新的重构分支,在多人开发下,在此分支上兼并代码冲突
  • 第三步,对正在并行进行迭代需求分支,进行的新增老路由调用进行记录,需求在迭代版别时兼并到版别主分支中,其首要思路为进行中的事务需求分支树立对应的事务需求路由替换分支,每个独自的事务需求路由分支可随事务需求发包提测,其中事务同学也参加路由的替换开发工作。
  • 第四步,准备集成测验时,将一切事务路由分支往当时迭代总路由分支提MR,比方biz1-Router向6648-SpringRouter提MR, 承认一切事务路由分支都兼并完结后,再向当时的事务迭代分支提MR。终究打包提测。
  1. 组件化架构分层

货拉拉货运iOS用户端架构优化实践

一个好的分层,在依靠方向需是由上依靠下,不能形成网状结构。 各事务线能独立开发、发布、上线。为到达这个方针,将整个用户端工程分为5个层次:

  • 壳工程:和一切以cocoapod办理的工程项目一样,壳工程做为当时用户端打包发布,是整个项目工程里最安稳的一层。
  • 事务线层: 用户端的事务首要包含搬迁事务、同城/跨城事务以及公共的地图选扯三大事务,别离归属于不同的事务开发团队担任。
  • 接口中台层:将一切事务组件的接口进行会集办理,各组件之前调用,都以协议接口办法进行API的调用,完全堵截组件间的联系。这其中包含:事务线接口、根底事务服务接口、根底才能服务接口、SDK适配器接口。
  • 服务层:依据服务协议接口的具体的组件完结,可为独立的pod组件,也可为某个事务线下某个子pod,其中服务包含两大类别:根底才能服务和根底事务服务。
  • 根底层:供给最根底的东西类或模块,包含自研的二方库以及开源的三方库,将一些共性的才能进行组合,规划成高度集群功用包。
  1. 单仓多组件计划

5.1 分仓规划的全体思路

货拉拉货运iOS用户端架构优化实践

  • 各事务团队独立担任库房事务的处理与发布,所做改变不会影响到其它事务团队。交互式协议接口在外,例如搬迁团队担任SHMove库房的办理工作,地图团队担任SHMap库房的办理工作,同城&跨城团队担任SHUser库房的办理工作。
  • Huolala库房用于办理打包相关,无事务相关的代码,能够确保根本无变动,App版别信息更新在外
  • Move库房用于搬迁独立事务,会以pod分支办法依靠到项目中来。
  • Map库房用于地图独立事务,会以pod tag办法依靠到项目中来。
  • User用于同城、跨城事务,会以pod分支办法依靠到项目中来。
  • 其它的二方、三方 pod库,一致由PodDepeLock进行会集式一致办理。

5.2 单仓多组件规划思路

货拉拉货运iOS用户端架构优化实践

  • 主导思想:事务模块上浮,东西模块下沉
  • 事务库房(User.git)办理一切的事务或事务域的组件,将可抽离的东西属于相关的模块、组件下沉到东西仓(CommonModule.git)中。将User做为一个独立的大组件,用于对外供给组件接口才能,内部将细分为4个子组件,每个子组件并不只是以文件夹办法办理,而是以独立组件办法进行集成,可独立进行编译、调试开发。
  • 东西仓(CommonModule.git)办理一切的东西模块以及App的生命周期使命处理,各区分的模块是子组件办法在东西仓组件中进行区分,并身不再是独立的组件。

四、结项收益

1、专项进程

专项从2022.3月到2022.7月,阅历了5个版别的迭代,历时4个月时刻,共有6位搭档间断性的参加了专项开发。经过相互的合作,分工合作终究完结了重构优化。

共完结34个组件128个组件办法的组件路由重构工作,完结20个组件模块的库房组件迁移工作。共编写85个测验用例,完结自测工作。

2、结项收益

2.1、Pod库版别冲突问题降低

  • 在施行Pod版别依靠办理计划之前,经常性出现 Pod版别不一致导致失利,需花费时刻去排查报错的问题。
  • 在施行此计划之后,根本没有因Pod版别依靠冲突,阻断开发的问题。

2.2、现有路由器运转时功率提高50%

Iphone6机型/Debug办法下的数据输出

  • 原路由器SHMedium依据tager-action办法,在运转时的50次平均耗时 0.846ms
  • 现路由器SHSpringRouter依据Protocol-class办法,在敞开组件服务缓存办法下50次平均耗时0.418ms

2.3、组件接口集成功率提高

原依据Medium路由办法下的组件,调用组件名、办法标识采用字符串传入办法,经过运用整数标识不同的组件办法名,不便于问题的定位与查找, 无法了解当时调用的数字所代表的事务逻辑。

集成或排查根本流程:

  • 找到要运用组件名字符串
  • 经过查找,定位到当时组件类名文件。
  • 经过传入办法数字标识符,查找组件类名中相应的办法
  • 调查当时办法需求哪些参数
  • 终究完结组件的调用或事务逻辑的流通。

整个流程下来,需求花费1分钟左右。

重构后的依据SHSpringRouter组件,将接口和完结进行别离,每个集成分只需求重视协议接口API,即可完结代码开发的工作。

集成或调用代码走读流程如下:

  • 排查流程只需求点击办法,即可跳转到完结处。
  • 调用API,只需求运用快速宏SHRouter(),即可完结组件API的调用。而且每个依据协议接口的API都有具体的文档阐明。
  • 整个流程下来,根本10s,就完结了。

2.4、事务代码提交功率提高

将事务相关的代码迁移到事务库房,让开发同学只专心针对事务开发,不需求跨库房写事务代码,节省了建分支、提MR的时刻,提高全体的开发功率。

2.5、无线上问题,各施行计划安稳运转

整个专项共区分为4期,阅历了5个迭代版别,完结共15个事务需求分支的代码兼并。运转期间无线上相关的问题。实时监控和Carsh渠道无相关的反常上报。

五、总结

1、遇到的问题

1.1、组件协议接口参数标准问题

在进行组件重构进程中,组件协议接口的参数直接复用原组件的参数办法,但后续改造进程发现,这种固定的形参办法功率低,协议办法能够做得更简洁明了,于是经过小组同学的讨论和投票,拟定了一套针对于组件协议接口的标准,包含接口命名、参数类型一系列的界说。

1.2、单仓多组件办法下图片资源获取问题

进行单仓多组件改造进程中,在单元测验阶段发现SHKit东西库供给的SHImageNamed(“”) 无法正常的获取到图片,经过对源码的梳理,发现是由于多层级组件路径匹配引起的。

考虑到工程有很多的模块都运用SHImageNamed(),决定对SHImageNamed()进行适配改造,修正正则表达式以及相关逻辑判断后,得以正常获取组件内的图片资源。

为防止后续开发会有图片获取反常,经过设置回调署理,将图片获取时的反常上报到实时监控上,能够发现线上的问题。

1.3、事务迭代,分支兼并问题

在对SHUser库房和SHCommoudle库房代码迁移时,发现后续与各事务迭代需求进行代码兼并是个极端头痛的事,比方一个组件本来是在SHCommoudle库房,可现在移动到了SHUser库房里,这种代码兼并无法经过git办法进行,需求人工的进行代码的兼并,极端容易出错。后续经过剖析并整理了事务分支代码兼并流程,与各事务同学共同一同兼并代码,完结了迭代需求的上线。

1.4、监控系统建造

路由替换与单仓多组件改造是两个风险系数极高的使命,咱们经过监控系统以及很多单元测验用例保持上线的质量、削减重构带来的风险。

2、不足与考虑

  • 专项前期规划不足,技能预研时刻跨度有点长

  • 拆分的组件没有进一步的细化和抽离

    • 考虑到专项时刻周期和事务安稳性,组件进一步的抽离将放到后续需求迭代版别中由事务同学进行。
    • 对于细化的方针,粗粒度怎么、怎么衡量,还没有一致把控度,后续将依据事务组件自身的杂乱度以及别离程度进行区分。
  • 组件间路由通信现在没有处理模型传递的问题

    • 原规划是想经过通用模型,处理不同组件间参数传递因模型引发组件耦合的问题。但实际操作中发现通用模型带来了其它的问题:通用模型臃肿,接口参数传递不清晰。
    • 对于此问题还需进一步研讨更好的计划。如读者有更好的主张,欢迎谈论辅导。
  • 项目中还存在一些的Common模块

    • 不要让Common出现以及怎样在事务代码规划中避免陷入到Common圈套里没有想好处理计划,如读者有更好的主张,欢迎谈论辅导。

六、参考资料

iOS 组件化计划选型

iOS使用架构谈 组件化计划

阿里组件化结构BeeHive解析

iOS 组件化/模块化架构规划实践

蜂鸟商家版 iOS 组件化 / 模块化实践总结

ENGINEERING THE ARCHITECTURE BEHIND UBER’S NEW RIDER APP