设计合理的架构

简单架构向大型项目架构演进中,解决三个问题,即:模块粒度应该如何划分?如何分层?多团队如何协作?而在这其中,模块粒度的划分是架构设计中非常关键的一步。同时,这也是一个细活,我们最好可以在不同阶段采用不同的粒度划分模块。

模块粒度应该怎么划分

模块划分为什么要遵循原则

项目规模变大后,模块划分必须遵循一定的原则。如果模块划分规则不规范、不清晰,就会导致代码耦合严重的问题,并加大架构重构的难度。这些问题主要表现在:

  • 业务需求不断,业务开发不能停。重新划分模块的工作量越大,成本越高,重构技改需求排上日程的难度也就越大。
  • 老业务代码年久失修,没有注释,修改起来需要重新梳理逻辑和关系,耗时长。

模块划分原则

对于 iOS 这种面向对象编程的开发模式来说,我们应该遵循以下五个原则,即 SOLID 原则。

  • 单一功能原则:对象功能要单一,不要在一个对象里添加很多功能。
  • 开闭原则:扩展是开放的,修改是封闭的。
  • 里氏替换原则:子类对象是可以替代基类对象的。
  • 接口隔离原则:接口的用途要单一,不要在一个接口上根据不同入参实现多个功能。
  • 依赖反转原则:方法应该依赖抽象,不要依赖实例。iOS 开发就是高层业务方法依赖于协议。

同时,遵守这五个原则是开发出容易维护和扩展的架构的基础。

选择合适的模块粒度

iOS 开发中的组件,不是 UI 的控件,也不是 ViewController 这种大 UI 和功能的集合。因为,UI 控件的粒度太小,而页面的粒度又太大。iOS 组件,应该是包含 UI 控件、相关多个小功能的合集,是一种粒度适中的模块。

并且,采用组件的话,对于代码逻辑和模块间的通信方式的改动都不大,完成老代码切换也就相对容易些。我们可以先按照物理划分,也就是将多个相同功能的类移动到同一个文件夹下,然后做成 CocoaPods 的包进行管理。

但是,仅做到这一步还不够,因为功能模块之间的耦合还是没有被解除。如果没有解除耦合关系的话,不同功能的开发还是没法独立开来,勉强开发完成后的影响范围评估也难以确定。

模块分层

  • 底层可以是与业务无关的基础组件,比如网络和存储等;
  • 中间层一般是通用的业务组件,比如账号、埋点、支付、购物车等;
  • 最上层是迭代业务组件,更新频率最高。

但是并不是所有的功能都做成组件,只有那些会被多个业务或者团队使用的功能模块才需要做成组件。因为,改造成组件也是需要时间成本的,很少有公司愿意完全停下业务去进行重构,而一旦决定某业务功能模块要改成组件,就要抓住机会,严格按照 SOLID 原则去改造组件,因为返工和再优化的机会可能不会再有。

#多团队之间如何分工

在代码层面,我们通过组件化解决了大项目、多人、多团队架构的问题,但是架构问题还涉及到团队人员结构上的架构。当公司或者集团的 App 多了后,相应的团队也就多了,为了能够让产品快速迭代和稳定发展,也需要一个合理的团队结构。

  • 首先,需要一个专门的基建团队,负责业务无关的基础功能组件和业务相关通用业务组件的开发。
  • 然后,每个业务都由一个专门的团队来负责开发。业务可以按照功能耦合度来划分,耦合度高的业务可以划分成单独的业务团队。
  • 基建团队人员应该是流动的,从业务团队里来,再回到业务团队中去。这么设计是因为业务团队和基建团队的边界不应该非常明显,否则就会出现基建团队埋头苦干,结果可能是做得过多、做得不够,或着功能不好用的问题,造成严重的资源浪费。

总结来讲,我想说的是团队分工要灵活,不要把人员隔离固化了,否则各干各的,做的东西相互都不用。核心上,团队分工还是要围绕着具体业务进行功能模块提炼,去解决重复建设的问题,在这个基础上把提炼出的模块做精做扎实。否则,拉一帮子人臆想出来的东西,无人问津,那就是把自己架空了。

架构设计方案

协议式架构

主要采用的是协议式编程的思路:在编译层面使用协议定义规范,实现可在不同地方,从而达到分布管理和维护组件的目的。这种方式也遵循了依赖反转原则,是一种很好的面向对象编程的实践。

但是,这个方案的缺点也很明显,主要体现在以下两个方面:

  • 由于协议式编程缺少统一调度层,导致难于集中管理,特别是项目规模变大、团队变多的情况下,架构管控就会显得越来越重要。

  • 协议式编程接口定义模式过于规范,从而使得架构的灵活性不够高。当需要引入一个新的设计模式来开发时,我们就会发现很难融入到当前架构中,缺乏架构的统一性。

虽然协议式架构有这两方面的局限性,但由于其简单易用的特点依然被很多公司采用。

中间者架构

采用中间者统一管理的方式,来控制 App 的整个生命周期中组件间的调用关系。同时,iOS 对于组件接口的设计也需要保持一致性,方便中间者统一调用。