前言

对于中大型挪动端APP开发来讲,组件化是一种罕用的我的项目架构形式。集体最近几年在工作我的项目中也始终应用组件化的形式来开发,在这过程中也积攒了一些教训和思考。次要是来自在日常开发中应用组件化开发遇到的问题以及和其余开发同学的交换探讨。

本文通过以下问题来介绍组件化这种开发架构的思维和常见的一些问题:

  • 为什么须要组件化
  • 组件化过程中会遇到的挑战和抉择
  • 如何保护一个高质量的组件化我的项目
提醒:本文说的组件化工程是指Multirepo应用独立的git仓库来治理组件。

组件化能够带来什么

繁多工程架构遇到的问题

在组件化架构之前,传统应用的工程架构次要是以Monolithic形式的繁多工程架构,也就是将所有代码放在单个代码仓库里治理。繁多工程架构应用了这么多年为什么忽然遇到了问题,这也引入了APP我的项目开发的一个大背景,现有中大型APP我的项目变的越来越简单:

  • 多APP我的项目并存 - 团体外部存在多个APP我的项目,不同APP心愿能够复用现有组件能力疾速搭建出新的APP
  • 性能增多 - 随着我的项目性能越来越多,代码量增多。同时须要更多的开发人员参加到我的项目中,这会减少开发团队之间合作的老本。
  • 多语言/多技术栈 - 引入了更多的新技术,例如应用一种以上的跨平台UI技术用于疾速交付业务,不同的编程语言、音视频、跨平台框架,减少了整个工程的复杂度。

以上这些业务倒退的诉求就给传统繁多工程架构形式带来了很多新的技术要求:

工程效率
  • 工程代码量过大会导致编译速度迟缓。
  • git工程提交同时可能带来更多的git提交抵触编译谬误
品质问题
  • 如何将git提交关联到对应的功能模块需要。发版时进行合规查看防止带入不标准的代码,对整个功能模块回滚的诉求。
  • 如何在单仓库中管控这么多开发人员的代码权限,尽可能防止不平安的提交并且限度改变范畴。
更大范畴的组件复用
  • 根底组件从反对单个APP复用到反对多个APP复用。
  • 不只是根底能力组件,对于业务能力组件也须要反对复用。(例如一个页面组件同时在多个APP应用)
  • 跨平台容器须要复用底层组件能力防止反复开发,同时不同跨平台容器API须要尽量放弃对立,底层基础设施向容器化倒退反对业务跨APP复用。
跨技术栈通信
  • 因为页面导航多技术栈混合共存,页面路由须要反对跨技术栈。
  • 跨组件通信须要反对跨语言/跨技术栈通信。
更好的解耦
  • 页面解耦。因为页面导航栈混合共存,页面本身不再清晰的晓得上游和上游页面由什么技术栈搭建,所以页面路由须要做到齐全解耦隔离技术栈的具体实现。
  • 业务组件间维持松耦合关系,能够灵便增加/移除,基于现有组件能力疾速搭建出不同的APP。
  • 对于同一个服务或页面能够插件化形式灵便提供多种不同的实现,不同的APP宿主也能够提供不同的实现并且提供A/B能力。
  • 因为包体积限度和不同组件蕴含雷同符号导致的符号抵触问题,在复用组件的时候须要尽可能引入最小依赖准则升高接入老本。

组件化架构的劣势

基于以上这些问题,当初的组件化架构心愿能够解决这些问题晋升整个交付效率和交付品质

组件化架构通常具备以下长处:

  • 代码复用 - 性能封装成组件更容易复用到不同的我的项目中,间接复用能够进步开发效率。并且每个组件职责繁多应用时会带入最小的依赖。
  • 升高了解复杂度 - 工程拆分为小组件当前,对于组件应用方咱们只须要通过组件对外裸露的公开API去应用组件的性能,不须要了解它外部的具体实现。这样能够帮忙咱们更容易了解整个大的我的项目工程。
  • 更好的解耦 - 在传统繁多工程项目中,尽管咱们能够应用设计模式或者编码标准来束缚模块间的依赖关系,然而因为都寄存在繁多工程目录中短少清晰的模块边界仍然无奈防止不衰弱的依赖关系。组件化当前能够明确定义须要对外裸露的能力,对于模块间的依赖关系咱们能够进行强束缚限度依赖,更好的做到解耦。对一个模块的增加和移除都会更容易,并且模块间的依赖关系更加清晰。
  • 隔离技术栈 - 不同的组件能够应用不同的编程语言/技术栈,并且不必放心会影响到其余组件或主工程。例如在不同的组件内能够自由选择应用KotlinSwift,能够应用不同的跨平台框架,只须要通过标准的形式暴露出页面路由或者服务办法即可。
  • 独立开发/保护/公布 - 大型项目通常有很多团队。在传统繁多我的项目集成打包时可能会遇到代码提交/分支合并的抵触问题。组件化当前每个团队负责本人的组件,组件能够独立开发/保护/公布晋升开发效率。
  • 进步编译/构建速度 - 因为组件会提前编译公布成二进制库进行依赖应用,相比编译全副源代码能够节俭大量的编译耗时。同时在日常组件开发时只须要编译大量依赖组件,相比繁多工程能够缩小大量的编译耗时和编译谬误。
  • 管控代码权限 - 通过组件化将代码拆分到不同组件git仓库中,咱们能够更好的管控代码权限和限度代码变更范畴。
  • 治理版本变更 - 咱们通常会应用CocoaPods/Gradle这类依赖管理工具来治理我的项目中所有的组件依赖。因为每一个组件都有一个明确的版本,这样咱们能够通过比照APP不同版本打包时的组件依赖表很清晰的辨认组件版本个性的变更,防止带入不合规的组件版本个性。并且在呈现问题时也很不便通过配置表进行回滚撤回。
提醒:组件化架构是为了解决繁多工程架构开发中的问题。如果你的我的项目中也会遇到这些痛点,那可能就须要做组件化

组件化遇到的挑战

尽管组件化架构能够带来这么多收益,但不是只有应用组件化架构就能够解决所有问题。通常来讲当咱们应用一种新的技术计划解决现有问题的时候也会带来一些新的问题,组件化架构能带来多少收益次要取决于整个工程组件化的品质。那在组件化架构中咱们如何去评估我的项目工程的组件化架构品质,咱们须要关注哪些问题。对于软件架构来讲,最重要的就是治理组件实体以及组件间的关系。所以对于组件化架构来讲次要是关注以下三个问题:

  • 如何划分组件的粒度、组件职责边界在哪里?
  • 组件间的依赖关系应该如何治理?
  • 组件间应该应用哪种形式调用和通信?

1. 组件拆分的粒度、组件职责边界在哪里?

某种程度上组件拆分粒度也是一种均衡的艺术,咱们须要在效率品质之间找到一种绝对的均衡。组件拆分粒度太粗:导致组件间耦合严密,并不能利用更好的复用/解耦/进步编译速度这些劣势。组件拆分粒度太细:导致须要保护更多的组件代码仓库、性能变更可能波及多个组件代码的批改/公布,这些都会带来额定的老本,同时组件过多也会导致组件依赖查找过程变的更简单更慢。

组件的职责也会影响咱们对于组件的拆分形式:每个组件的定位是什么,应该蕴含什么样的性能,是否能够被复用,增加某个性能的时候应该创立新组件还是增加到现有组件,当组件简单到肯定水平时是否须要拆分出新个组件。

在拆分组件前须要提前去思考这些问题。

2. 组件间的依赖关系应该如何治理?

组件间的依赖形式次要分为间接强耦合依赖间接松耦合依赖强耦合依赖是对依赖的组件间接应用对应的API进行调用,这种调用形式长处是简略间接性能更好,毛病是一种齐全耦合的调用形式。(根底组件通常应用这种形式)。松耦合依赖次要是通过告诉URL SchemeObjC Runtime服务接口事件队列等通信形式进行间接依赖调用。尽管性能绝对差一点,但这是一种绝对耦合水平比拟低并且灵便的依赖形式。(业务组件通常应用这种形式)

组件间的依赖关系很重要是因为在长期的我的项目开发演化过程中很容易造成一种简单的网状依赖关系。尽管看似应用组件化的形式将模块拆分成不同的组件,然而组件间可能存在很多互相穿插的依赖耦合关系,很多组件都被其余组件间接依赖隐式间接依赖。这样咱们就背离了组件化架构更好的解耦、更好的复用、更疾速的开发/编译/公布的初衷。

所以咱们须要制订一套标准去束缚和标准组件间的依赖关系:两个组件之间是否能够依赖,组件间依赖方向,抉择强耦合依赖还是松耦合依赖。

3. 组件间松耦合依赖关系应该应用哪种形式调用和通信?

松耦合依赖通常能够应用告诉URL SchemeObjC Runtime服务接口事件队列等形式通信进行间接调用,然而应用哪种形式更好业界也有很多争执,并且每种形式都有一些优缺点。通常在我的项目中会依据不同的应用场景至多会抉择2种通信形式。

耦合水平低的形式例如URL Scheme,能够做到齐全解耦绝对比拟灵便。然而无奈利用编译时查看无奈传递简单对象、调用方/被调用方都须要对参数做大量的正确性检查和对齐。同时可能无奈检测对应的调用办法是否存在。

耦合水平高的形式例如服务接口,须要对服务接口办法进行强依赖,然而能够利用编译时查看、传递简单对象、并且能够更好的反对Swift个性。

咱们须要在解耦水平容易应用平安上找到一种适合的形式。

提醒:这里的耦合水平高是绝对于耦合水平低的形式进行比拟,相比间接依赖对应组件仍然是一种耦合水平低的依赖关系。

组件化架构实际标准和准则

基于以上这些组件化架构的问题,须要一些组件化架构相干的标准和准则帮忙咱们做好组件化架构,前面次要会围绕以下三点进行介绍:

  • 组件拆分准则 - 拆分思维和最佳实际领导组件拆分
  • 组件间依赖 - 优化组件间依赖关系跨组件调用/通信形式的抉择
  • 品质保障 - 防止在继续的工程演化过程中工程质量逐步劣化。次要蕴含平安卡口和CI查看

工程实例

接下来以一个典型的电商APP架构案例来介绍一个组件化工程。这个案例架构具备之前所说现有中大型APP架构的一些特点,多组件、多技术栈、业务间须要解耦、复用底层根底组件。基于这个案例来介绍下面的三点准则。 

组件拆分准则


组件拆分最重要是帮咱们梳理出组件职责以及组件职责的边界。组件划分也会应用很多通用的设计准则和架构思维。

应用分层思维拆分

通常咱们能够首先应用分层架构的思维将所有组件纵向拆分为多层组件,下面层级的组件只能依赖上面层级的组件。个别至多能够划分为四层组件

  • 根底层 - 提供外围的与下层业务无关的根底能力。能够被下层组件间接依赖应用。
  • 业务公共层 - 次要蕴含页面路由、公共UI组件、跨组件通信以及服务接口,可被下层组件间接依赖应用。
  • 业务实现层 - 业务外围实现层,蕴含原生页面、跨平台容器、业务服务实现。组件间不能间接依赖,只能通过调用页面路由或跨组件通信组件进行应用。
  • APP宿主层 - 次要蕴含APP主工程、启动流程、页面路由注册、服务注册、SDK参数初始化等组件,用于构建打包生成相应的APP

划分层级能够很好的领导咱们进行组件拆分。在拆分组件时咱们须要先辨认它应该在哪一层,它应该以哪种调用形式被其余组件应用,新增加的性能是否会产生反向依赖,帮忙咱们标准组件间的依赖关系。同时按层级拆分组件也有利于底层根底组件的复用

以下场景应用分层思维就很容易辨认:

根底组件依赖业务组件

例子:APP内业务发动网络申请通常须要携带公共参数/Cookie

  • 没有组件分层束缚 - 网络库可能会依赖登录服务获取用户信息、依赖定位服务获取经纬度,引入大量的依赖变成业务组件。
  • 有组件分层束缚 - 网络库作为一个根底组件,它不须要关注下层业务须要携带哪些公共业务参数,同时登录/定位服务组件在网络库下层不能被反向依赖。这时候会思考独自创立一个公共参数治理类,在APP运行时监听各种状态的变更并调用网络库更新公共参数/Cookie
业务组件间依赖方向是否正确

登录状态切换常常会波及到很多业务逻辑的触发,例如清空本地用户缓存、地址缓存、清空购物车数据、UI状态变更。

  • 没有组件分层束缚 - 可能会在登录服务内当登录状态切换时调用多个业务逻辑的触发,导致登录服务引入多个业务组件依赖。
  • 有组件分层束缚 - 登录组件只须要在登录状态切换时发出通知,无需晓得登录状态切换会影响哪些业务。业务逻辑应该监听登录状态的变更。
辨认根底组件还是业务组件

尽管很多场景下咱们很容易能辨认解决进去一个性能应该归属于根底组件还是业务组件,例如一个UI控件是根底组件还是业务组件。然而很多时候边界又十分的含糊,例如一个增加购物车按键应该是一个根底组件还是业务组件呢。

  • 根底组件 - 如果不须要依赖业务公共层那该当划分为一个根底组件。
  • 业务组件 - 依赖了业务公共层或者网络库,那就应该划分为一个业务组件。

分层思维能够很好的帮忙咱们治理组件间的依赖关系,并且明确每个组件的职责边界。

根底/业务组件拆分准则

划分根底/业务组件次要是为了强制束缚组件间的依赖关系。以下面的组件分层架构为例:

  • 根底组件 - 根底组件被间接依赖应用,应用方调用根底组件对外裸露API间接应用。根底层业务公共层都为根底组件。
  • 业务组件 - 业务组件不可被间接依赖应用,只能通过间接通信形式进行应用。APP宿主层业务实现层都为业务组件。
提醒:这里的业务组件并不蕴含业务UI组件

根底组件拆分

根底组件通常依据职责繁多准则进行拆分比拟容易拆分,然而会有一些拆分场景须要思考:

应用插件组件拆分根底组件扩大能力

将外围根底能力和扩大能力拆分到不同的组件。以网络库为例,除了提供最外围的接口申请能力,同时可能还蕴含一些扩大能力例如HTTPDNS网络性能检测弱网优化等能力。但这些扩大能力放在网络库组件外部可能会导致以下问题:

  • 扩大能力会使组件本身代码变的更加简单。
  • 应用方不肯定会应用所有这些扩大能力违反了最小依赖准则。带来更多的包体积,引入更多的组件依赖,减少模块间的耦合度。
  • 相干的扩大能力不反对灵便的替换/插拔。

所以这种场景咱们能够思考依据理论状况将扩大能力拆分到相应的插件组件,应用方须要时再依赖引入对应插件组件。

业务组件拆分

业务页面拆分形式

针对业务页面能够应用技术栈业务域页面粒度三种形式进行更细粒度的划分,通常至多要拆分到技术栈业务域这一层级,页面粒度拆分依据具体页面复杂度和复用诉求。

  • 基于技术栈进行拆分 - 不同的技术栈须要拆分到不同的组件进行治理。
  • 基于业务域进行拆分 - 将同一个业务域的所有页面拆分一个组件,防止不同业务域之间造成强耦合依赖关系,同一个业务域通常会有更多复用和通信的场景也不便开发。例如订单详情和订单列表可搁置在一起治理。
  • 基于页面粒度进行拆分 - 单个页面简单度过高或须要被独自复用时须要拆分到一个单个组件治理。
提醒:搁置在繁多组件内的多个页面之间也应适当升高耦合水平。

第三方库

第三方库应拆分独自组件治理

第三方库应应用独立的组件进行治理,一方面有利于组件复用同时防止多个反复第三方库导致符号抵触,另一方面有利于后续降级保护。

一些提醒

缩小应用通用聚合公共组件

为了防止拆分过多的组件,咱们通常会创立聚合组件将一些代码量不多/性能类似的类放到同一个组件内,例如Foundation组件UI组件。然而很多时候会存在滥用的场景,该当警觉这类公共聚合组件。上面是一些公共聚合组件容易滥用的场景:

  • 增加一个新性能不晓得该当加在哪里时,就加到公共聚合组件内,工夫久了当前公共组件依赖特地多。
  • 公共组件增加了一个非常复杂的能力,导致复杂度变高或者引入大量依赖
  • 太多能力聚合到一起。例如将网络库、图片库这些能力放在同一个组件内
  • 根底/业务UI组件没有拆分。根底UI组件通常只提供最根底的UI和十分轻量的逻辑,业务组件通常会充当根底UI组件的数据源以及业务逻辑。

然而也不能完全避免应用聚合公共组件,不然会导致产生更多的小组件减少保护老本。然而咱们将一个能力增加到公共聚合组件时能够依据以下几个条件来衡量:

  • 是否会引入大量新的依赖
  • 性能复杂度、代码数量,太简单的不应该增加到公共组件
  • 能力是否须要被独自复用,须要独自复用就不应该增加到公共组件
第三方库思考不间接对外裸露应用

当存在以下状况时可思考对第三方库进行适当的封装防止间接裸露第三方库:

  • 应用方通常只须要应用大量API,第三方库会对外裸露大量API减少应用难度,同时可能导致一些平安问题
  • 对外暗藏具体实现,不便后续更换其余第三方库、自实现、第三方库产生Break Change变更时降级更容易
  • 须要封装扩大一些能力让应用方应用起来更容易

以网络库为例:1.通常须要对接公司外部的API网关能力所以须要适当做一些封装,例如签名或者加密策略。2.应用方通常只须要用到一个通用的申请办法无需对外裸露太多API。3.为了平安通常须要对业务方暗藏一些办法防止谬误调用,例如全局Cookie批改等能力。4.对外暗藏具体第三方库能够不便变更。

第三方库尽可能防止间接批改源码

第三方库组件尽可能不要间接批改源码,除修复Bug/Crash之外尽可能防止带入其余性能代码导致前面更新艰难。须要增加性能时能够通过在其余组件内应用第三方库对外裸露的API进行能力扩大。

组件间依赖关系

业务组件间通信形式抉择

松耦合通信形式比照

基于以上表格中各种计划的优缺点,集体举荐应用URL Scheme协定作为页面路由通信形式,应用服务接口提供业务性能服务。告诉订阅场景可应用告诉RxSwift形式提供一对多的订阅能力。

服务接口

服务接口对应的实现和页面是否须要拆分

以购物车服务为例,购物车接口服务提供了增加购物车的能力。加车服务具体的实现应该放在购物车页面组件内还是独立进去搁置在独自的组件。将购物车服务实现和购物车页面拆分的长处是购物车服务和购物车页面更好的解耦,都能独自反对复用。毛病是开发效率升高,批改购物车性能时可能会波及到同时批改购物车服务组件和购物车页面组件。

所以在须要独自复用服务页面的场景时可思考别离拆分出单个组件(例如购物车服务作为一种通用能力提供给下层跨平台容器能力)。但即便在同一个组件内也倡议对服务和页面应用分层设计的形式进行解耦。

服务接口是否须要拆分

个别我的项目可能至多会有10+个服务接口,这些服务接口应该对立寄存在单个组件还是每个接口对应一个组件。

  • 对立寄存:长处是一起治理更快捷不便。毛病是所有接口对应一个组件版本,不能反对繁多接口应用不同版本,不利于须要跨APP复用的我的项目。并且应用方可能会引入大量无用的接口依赖。
  • 离开寄存:长处是每个接口可应用不同的版本并且应用方只须要依赖特定的接口。毛病是会产生更多的组件仓库,组件数量也会减少依赖查找的耗时。
    所以大型项目抉择离开寄存的形式治理接口绝对更适合一点。也能够思考将大部分最外围的服务接口搁置到一起治理。
反对Swift的服务接口实现举荐

应用Swift实现传统的服务接口模式通常会遇到以下两个问题:

  • 接口须要同时反对Objective-CSwift调用,同时心愿应用Swift个性设计API。如何实现Objective-CSwift协定能够复用一个实例
  • Swift对于动态性反对比拟弱,纯Swift类无奈反对运行时动态创建只能在注册时创立实例

基于以上问题,集体举荐应用上面的形式实现接口服务模式:

  • 应用Objective-C协定提供最根底的服务能力,之后创立Swift协定扩大提供局部Swift个性的API
  • 接口实现类继承NSObject反对运行时动静初始化
// @objc协定@objc public protocol JDCartService {    func addCart(request: JDAddCartRequest, onSuccess: () -> Void, onFail: () ->) }// swift协定public protocol CartService: JDCartService {        func addCart() async    func addCart(onCompletion: Result)}// 实现类class CartServiceImp: NSObject, CartService {    // 同时实现Objc和Swift协定}
服务应该中心化注册还是分布式注册

中心化注册是在宿主APP启动时对立注册服务接口的对应实现实例,分布式注册是在组件内组件本身进行注册。集体举荐中心化注册的形式在宿主APP启动时对立进行注册治理,明确服务的实现方更清晰,同时防止不同组件蕴含同一个服务接口的不同实例导致的抵触。

组件版本兼容

审慎应用常量、枚举、宏

因为组件编译公布的时候会生成二进制库,编译器会将依赖的常量、枚举、宏替换成对应的值或代码,所以当后续这些常量、枚举、宏产生变更的时候,已生成的二进制库并不会扭转导致打包的时候仍然应用的旧值,必须从新公布应用这些值的组件才行。所以该当尽量避免批改常量、枚举、宏值,如果已知后续可能会变更的状况下应防止应用常量、枚举、宏

根底组件API向后兼容
  • 对外API需保障向后兼容,应用增加API的形式扩大现有能力,防止对原有API进行break change改变或移除
  • 应用对象封装传递参数和回调参数,防止对原有API进行批改
提醒:特地是对于Objective-C这类动静调用的语言来讲,打包构建时并不能发现调用的办法不存在、参数谬误这些问题。所以咱们该当尽可能防止现有办法的变更。同时也举荐更多应用Swift编译器能够发现这些问题提醒编译谬误。
缩小公布大版本

Cocoapods为例,组件公布大版本会导致依赖此组件的所有组件都必须同时降级到大的版本从新公布,这样会给组件应用放带来极大的更新老本。所以组件应该缩小公布大版本,除非必须强制所有组件肯定要降级。

优先选择接口服务缩小裸露View类

当只关注API提供的能力并不关注API提供的状态时尽可能通过API的形式来裸露能力。因为裸露接口办法相比视图View,调用方只须要依赖接口办法相比依赖View类能够更小化的依赖,同时接口对于实现方将来扩大能力更灵便。以抉择用户地址API为例,通常调用方并不关注实现方以弹窗的形式还是页面的形式提供交互能力让用户抉择,只关注用户最终抉择的地址数据。并且调用方不须要解决弹窗和页面的展现逻辑应用起来更不便,也便于实现方之后批改交互方式。

应用接口的办法
addressService.chooseAddress { address in}
应用View的形式
let addressView = AddressView()addressView.callback = { address in    ///}addressView.show()
防止应用Runtime反射动静调用类

该当尽量避免应用反射机制在运行时应用其余类,因为类的实现方不能保障这个类始终存在,编译器也无奈检测出谬误。某些基于AOP的性能可能会应用到这种动静反射能力,然而大部分场景应该尽量避免。

第三方库

第三方库组件不容许依赖其余组件。

第三方库组件不容许依赖其余组件。

品质保障

尽管后面讲到了很多标准和准则,然而并不能保障咱们的这些标准和准则能够强制执行。所以咱们须要在组件公布和利用打包阶段增加一些卡口平安检测,及时发现组件化依赖问题防止带入线上。

CI查看

组件公布

在组件公布时增加一个安全检查,防止不合乎依赖标准的组件公布胜利。通常咱们能够增加以下依赖查看规定:

  • 第三方库不可依赖其余组件
  • 根底组件不可依赖业务组件
  • 业务组件不可间接依赖业务组件
  • 组件间通常不可相互依赖
  • 不容许组件层级间反向依赖
版本集成标准

集成系统须要将特定需要和组件版本关联到一起,打包时会依据版本需要主动退出对应的组件版本。防止开发同学间接批改组件版本引入不应该退出到版本的个性。

打包构建

在宿主APP打包时,提前检测出接口服务存在的问题,防止带入到线上。通常能够检测以下问题:

  • 服务接口对应的实现类不存在
  • 服务接口对应的实现类没有实现所有办法
  • 应用ObjC Runtime动静调用类和办法

线上异样上报

线上查看能够帮忙咱们在灰度公布的及时发现问题及时修复,通常能够发现以下问题:

  • 路由跳转对应的页面不存在
  • 接口服务对应的实现类不存在
  • 接口服务对应的办法不存在

可量化指标

咱们能够通过一些指标来量化整个工程组件化的衰弱水平,以下列出常见的一些指标:

根底组件依赖数量

组件依赖的所有根底组件总数,当依赖的根底组件总数过高时应该及时进行重构。如果大量的业务组件都须要依赖十分多的根底组件,那可能阐明根底组件的依赖关系呈现了很大的问题,这时候须要对根底组件进行优化重构:

  • 思考应用接口服务对外裸露能力,组件层级须要晋升
  • 思考将局部能力拆分出为独立的新组件
业务服务依赖数量

业务组件对其余业务服务组件的依赖数量。当业务组件依赖了其余业务服务调用时也会造成隐式的耦合关系,依赖过多时该当思考是否应该对外裸露可监听变动的告诉订阅以订阅察看的形式代替被动调用

谬误依赖关系数量

谬误的依赖关系应该及时优化革新。

一些常见的问题

根底组件应该间接裸露还是应用接口对外裸露

根底组件应该间接应用头文件API裸露还是应用接口间接裸露有时候很难衡量,然而能够依据一些个性来衡量抉择:

API间接裸露
  • 性能繁多/依赖少 - 一些工具类,例如Foundation
  • API简单 - API十分多如果应用接口须要形象太多接口,例如网络库日志
  • UI组件 - 须要间接裸露UIViewUI组件,例如UIKit
接口对外裸露
  • 可扩展性 - 基于接口能够灵便替换不同的实现,例如定位能力能够应用零碎自带的API,也能够应用不同地图厂商的API
  • 缩小依赖引入 - 升高应用方的接入老本,进步日常开发/组件公布效率
  • 可插拔能力 - 对应的能力可移除,同时也不影响外围业务
    提醒:这些以接口对外裸露的API还有一个劣势是能够形象成容器化的API,造成对立的标准规范。应用方调用同样的API,不同的APP能够提供不一样的实现。

小我的项目是否应该做组件化

集体认为小我的项目也能够做组件化,须要关注的是须要做到什么水平的组件化。通常来讲越大型越简单的我的项目组件化拆分的粒度更细组件数越多。对于小我的项目来讲尽管晚期做组件化的收益并不大,也须要适当思考将来的发展趋势预留肯定的空间,同时也须要适当思考模块间的依赖关系防止前期拆分模块时很艰难。刚开始做粒度比拟粗的组件化,之后在我的项目倒退中一直的调整组件化的粒度。也能够思考应用相似Monorepo的形式来治理我的项目,代码都在一个仓库中治理,通过文件夹隔离标准模块间的依赖。

繁多工程如何革新为组件化工程

一般来讲咱们须要应用循序渐进逐渐重构的策略对原有我的项目进行革新,然而有一些模块能够优先被组件化拆分升高整个组件化的难度:

  • 优先拆分出最外围的所有业务模块可能都须要应用的组件,这些组件拆分实现当前能力为之后业务模块拆分提供根底。例如FoundationUI组件网络库图片库埋点日志等最根底的组件。
  • 优先拆分不被其余组件依赖被其余组件依赖较少的模块组件,这些模块绝对比拟独立拆分起来比拟高效并且对现有工程革新较小。例如性能监控微信SDK这类绝对独立的能力。

组件化带来的额定老本

组件化架构可能会带来以下这些额定的老本:

  • 治理更多的组件git仓库
  • 每次组件公布都须要从新编译/公布
  • 因为组件应用方都是应用相应的组件二进制库,所以调试源码会变的更艰难
  • 开发组件治理平台,治理组件版本、版本配置表等能力
  • 每个组件须要有本人的Example工程进行日常开发调试
  • 解决可能存在的组件版本不统一导致的依赖抵触、编译谬误等问题
  • 需要可能会波及到多组件改变,如何进行Code Review版本合入查看

Monorepo

我集体并没有在理论的我的项目中应用过Monorepo形式治理我的项目。Monorepo是将所有组件代码放在单个git仓库内治理,而后应用文件夹拆分为不同的组件。不同文件夹中的代码不能间接依赖应用,须要配置本地文件夹的组件依赖关系,在实现组件化的同时防止拆分太多的git仓库。不过集体认为Monorepo同时也须要解决以下几个问题:

  • 编译耗时优化 - 将所有源码放在单个工程中会导致编译变慢,所以必须优化现有工程编译流程,升高非必要的反复编译耗时。
  • 组件版本治理 - 在组件化工程中咱们能够通过配置组件的特定版本来治理性能是否合入到版本中,但在Monorepo中只能通过分支Merge Request来治理个性是否合入,回滚也会更加繁琐。
  • 高质量CI流程 - 在单个仓库中,当一个开发者有仓库权限时他就能够批改该仓库的任意代码。所以必须欠缺代码合入标准,更高标准的Code Review集成测试查看自动化查看防止问题代码带到线上。

总结

集体认为并不存在一个完满的架构,咱们本身的组织架构、业务、人员都在变动,架构也须要随着这个过程进行适当的调整和重构,最重要的是咱们能及时发现架构中存在的问题并且有志愿/能力去调整防止始终沉积变成更大的技术债权。

同时工程架构的扭转也会肯定水平的扭转开发人员的分工,对于大型工程来讲组件化的水平更高,每个开发人员的工作分工会更细。对于底层根底组件的开发,须要提供更多高性能/高质量的根底组件让下层业务开发人员更加效率的撑持业务,技术深度也会更加深刻。对于下层业务开发,更多是应用这些底层根底组件,同时可能也须要把握多种跨端UI技术栈疾速撑持业务,技术栈会更广然而不会太深刻。

作者:京东批发 何骁

起源:京东云开发者社区