Swift Package Manager 系列 07|我会怎样用 SPM 管理一个中型 iOS 项目
中型项目真正怕的不是模块不够多,而是模块存在了却没有稳定的依赖方向和职责边界
如果让我用 SPM 管一个中型 iOS 项目,我最先关注的不会是“拆多少个 Package”,而是这三个问题:
- 哪些能力真的值得独立
- 模块依赖怎么保持稳定方向
- 宿主工程到底该保留什么,不该保留什么
因为中型项目最容易掉进一个陷阱:
看起来已经开始模块化了,但实际上只是把原来主工程里的复杂度重新分布到了多个 Package。
所以这篇文章我更想讲的不是“推荐拆成几个模块”,而是我会怎样判断模块、层次和依赖图。
一、我不会一开始就追求“模块多”,而会先追求“层次清楚”
中型项目的复杂度通常还没大到必须极致细分,但已经足以让“全部堆在主工程里”开始变得危险。
这时候最重要的不是拆得多,而是先拆出层次感。
我通常会先看项目里能不能稳定分出下面几类东西:
- 基础能力层
- 领域能力层
- UI 复用层
- 宿主工程层
这四类东西一旦混在一起,项目规模一上来就很容易失控。
但如果层次先立住了,模块数量反而是后面的细节。
二、我会先分三类最核心的模块
1. 基础能力模块
例如:
- 网络
- 日志
- 缓存
- 配置读取
- 基础工具
这类模块的特点是离业务远、复用性强、依赖方向应该尽量向下封闭。
2. 领域能力模块
例如:
- 账号
- 用户
- 内容
- 订单
这些模块不是页面,而是业务能力中心。
它们应该承载领域模型、仓库、用例,而不是具体页面实现。
3. UI 复用模块
例如:
- 设计系统
- 通用组件
- 通用列表容器
这里我会很谨慎,避免把业务页面误拆成 UI 模块。
UI 复用模块更适合承载稳定、通用、跨页面的组件能力。
三、宿主工程应该保留什么
这是很多团队容易忽略的问题。
一旦开始做 SPM,很容易出现一种倾向:
“既然都模块化了,那宿主工程最好越薄越好。”
这句话只对一半。
宿主工程确实不该承载太多可复用能力,但它仍然应该保留一些天然属于宿主的东西,比如:
- App 生命周期编排
- 路由装配
- 环境配置注入
- 模块组装
- 最终产品形态相关代码
也就是说,宿主工程不是“什么都不放”,而是应该专注在装配和产品级协作上,而不是承载一堆本可下沉的能力。
四、我不会一开始就拆太细
这是我非常坚持的一条。
中型项目最容易掉进的坑之一,就是“为了模块化而模块化”,最后把系统拆得过碎。
模块一旦过碎,问题会立刻出现:
- 依赖图复杂
- 调试路径变长
- review 时跨模块跳转频繁
- 一点点改动就涉及多个包联动
这会让团队很快开始怀疑模块化本身。
所以我更倾向于:
- 先拆出少数职责清楚的大模块
- 观察几个迭代
- 再决定是否细化
第一次就把系统切得很细,通常不是成熟,而是着急。
五、依赖方向比模块数量更重要
如果让我在“多拆几个模块”和“把依赖方向理顺”之间选一个,我一定选后者。
因为模块多并不自动等于结构好。
真正决定系统能不能长期稳定演进的,是依赖方向是否清楚。
例如我更在意这些关系是否成立:
- 基础层不依赖业务层
- 领域层不反向依赖宿主层
- UI 复用层不偷带页面业务语义
- 宿主层负责装配,而不是把内部实现全都攥在手里
只要依赖方向乱了,模块再多,系统也只是在“多层嵌套的耦合”。
六、我会很警惕“万能公共模块”
这是中型项目里高频出现的反模式。
项目一开始做模块化时,经常会出现一个叫:
CommonSharedCoreBase
之类的超级模块。
如果这个模块承载的是明确基础能力,那没问题。
但很多项目里的“公共模块”最后会慢慢变成:
- 哪儿都能依赖
- 什么都往里放
- 既有工具函数,又有业务模型,又有 UI 组件
结果就是它反而成了新的中心耦合点。
所以我更愿意拆成职责明确的小基础模块,而不是造一个什么都兜底的大公共模块。
七、中型项目模块化最重要的不是技术,而是团队认知一致
这点非常现实。
中型项目里,很多结构问题不是工具不行,而是团队对边界没有一致理解。
例如有人觉得:
- 页面相关都算一个模块
有人觉得:
- 领域模型应该单独拆
有人觉得:
- 公共组件应该跟业务模块放一起更方便
如果这些认知长期不统一,那么你即使用了 SPM,系统也会不断长出边界冲突。
所以在中型项目里,我会很重视:
- 模块职责的命名是否一致
- 依赖方向是否有共识
- 新增代码时大家能不能判断应该放哪一层
这比单纯“拆出了多少包”重要得多。
八、结论:用 SPM 管中型项目,核心不在数量,而在依赖图是否长期稳定
如果只用一句话总结,我会说:
用 SPM 管理一个中型 iOS 项目,最重要的不是拆出多少个 Package,而是让基础能力、领域能力、UI 复用和宿主装配之间形成长期稳定的依赖方向。
做到这一点,模块数量自然会找到合适规模。
做不到这一点,拆再多也只是把复杂度重新切片。