技术探索与实践:从一次复杂项目重构说起
大家好,我是一名在一线互联网公司从事 iOS 开发的工程师。平时主要负责公司核心 App 的客户端架构设计和关键技术攻坚。今天我想借这篇文章,聊一聊我在一次重大技术重构项目中的经历和思考,谈谈我是如何进行技术探索与实践的。
这不仅是一次代码上的重构过程,更是一次对技术方向、团队协作以及产品演进策略的深度思考。希望通过我的分享,能给大家带来一些启发或借鉴。
项目背景:为什么需要技术重构?

我们开发的应用是一款面向全国用户的资讯类平台,经过多年的迭代,App 架构早已不再是最初的样子。早期为了快速上线而采用的 MVC 模式逐渐暴露出诸多问题:
- 页面逻辑臃肿,ViewController 膨胀严重
- 网络请求散落在各个业务中,难以统一管理
- 数据模型和 UI 组件高度耦合,导致复用困难
- 新功能开发周期越来越长,维护成本陡增
最明显的一个现象是:一个页面的 ViewController 文件常常超过 3000 行,修改一处地方往往会牵动多处逻辑,稍有不慎就会引入 bug。
于是我们在一次版本迭代中决定启动客户端架构升级计划,目标是从整体上解耦业务、提升可维护性,并为未来的技术演进打下基础。
遇到的挑战:不止是技术问题

项目一开始我们就遇到了几个关键问题:
1. 架构风格选择
当时主流的方案有几种:
- MVVM + Coordinator
- VIPER
- Redux(结合 Combine)
- Clean Architecture + Domain Driven Design
我们需要在有限的人力和时间内完成这次重构,同时不影响现有功能的正常交付。权衡之后,我们选择了 MVVM + Coordinator + Repository Pattern 的组合方式。
原因如下:
- MVVM 相比 MVC 更容易解耦,UI 和数据绑定清晰,iOS 原生支持(尤其是 SwiftUI 可以很好地利用);
- Coordinator 能够很好地管理页面导航逻辑,避免了传统 UIKit 中 VC 推来推去的混乱局面;
- Repository 模式可以统一数据来源,无论是本地缓存还是网络 API,都通过一层抽象屏蔽实现细节。
当然这个选择不是完美的,但对于我们当前项目的复杂度来说,是一个“足够好”的折中方案。
2. 新旧架构并行的问题
由于 App 体量很大,不可能一次性全部重构。因此我们必须保证新老模块可以共存,且能够互相调用。
这听起来简单,做起来却不容易。比如:
- 如何让 Coordinator 启动老的 VC?
- 如何在新架构里使用旧的数据结构?
- 新旧数据模型之间怎么转换?是彻底废弃老模型,还是写适配器?
我们为此制定了一套过渡机制:
- 入口点统一交给 Coordinator 管理,即使是老页面也能被纳入导航流;
- 使用 Adaptor 模式包装旧接口,确保新模块无需直接依赖老代码;
- 逐步替换 ViewModel,先从小型页面开始验证架构稳定性。
整个过程持续了大约三个月时间,期间我们保持每周两个迭代版本的节奏。
3. 团队协作的摩擦
架构变动必然带来流程的变化。初期很多同学不适应新的开发方式:
- 写一个页面要新建一堆文件(View、ViewModel、Router、Service)
- 不太理解为什么要拆得这么细
- 对 Coordinator 的使用方法不够熟悉,经常绕回去用 pushViewController
我们做了几件事来缓解这种冲突:
- 组织内部培训,演示重构前后的对比
- 编写一份清晰的架构指南文档,附带代码模板
- 设立 code review checklist,在审核中强调架构规范
- 安排 mentor 制度,让资深同学带动新人上手
这些措施起到了不错的效果,一段时间后大家也逐渐认同新架构的优势。
解决方案:架构设计与技术选型
下面我来详细说说我们的解决方案到底是什么样的。
整体架构设计图
+------------------+ +-------------------+
| View (VC) | ---> | ViewModel |
+------------------+ +---------+---------+
|
+---------v---------+
| Repository |
+---------+---------+
|
+---------------v-----------------+
| Network / Local Service |
+---------------------------------+
+------------------+
| Coordinator |
+------------------+
- View 层:仅负责 UI 渲染和事件响应,不再包含任何业务逻辑
- ViewModel 层:持有业务状态,对外暴露命令(Commands)和绑定属性(Bindable Values)
- Repository 层:数据访问层,屏蔽本地存储/网络调用差异
- Coordinator 层:导航控制中心,统一页面跳转逻辑
我们没有完全照搬社区流行的架构,而是根据业务特性做了调整,比如说:
- 在 ViewModel 中保留了一些轻量级状态管理,没有完全采用 Redux 或者单向数据流(那样会导致学习曲线过高)
- Coordinator 只管理页面跳转,不处理业务逻辑,这一点非常重要
- Repository 提供统一接口给 ViewModel 使用,屏蔽底层细节
技术细节与实现要点
ViewModel 的构建方式
我们使用了一个轻量级的 BaseViewModel 来封装公共行为:
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
class BaseViewModel<Input, Output>: NSObject, ViewModelType {
func transform(input: Input) -> Output {
fatalError("Subclass must override this method")
}
}
每个具体 ViewModel 实现 transform 方法,接收输入(通常是用户操作事件),返回输出(用于更新 UI)。
这样做的好处是职责划分明确,测试友好,也可以方便地集成 RxSwift 或 Combine。
Coordinator 的实现思路
我们使用泛型的方式定义一个基本的 Coordinator:
protocol Coordinator {
func start()
}
然后针对不同的功能模块派生具体的 Coordinator:
final class NewsListCoordinator: Coordinator {
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let viewModel = NewsListViewModel()
let viewController = NewsListViewController(viewModel: viewModel)
viewController.coordinator = self
navigationController.pushViewController(viewController, animated: true)
}
}
注意这里我们将 coordinator 注入给了 VC,这样 VC 就可以通过 coordinator 调用其他页面,而不是自己 push/pop。
这种方式避免了 VC 之间的紧耦合,使得导航逻辑更易维护。
Repository 的统一接口
我们定义了一个 NewsRepository 抽象接口:
protocol NewsRepository {
func fetchTopNews(completion: @escaping (Result<[News], Error>) -> Void)
}
然后分别实现了网络版和本地缓存版本:
class RemoteNewsRepository: NewsRepository { ... }
class LocalNewsRepository: NewsRepository { ... }
在 ViewModel 中注入的是抽象类型:
class NewsListViewModel {
private let repository: NewsRepository
init(repository: NewsRepository = DefaultRepositories.news) {
self.repository = repository
}
}
这样的分层设计让我们可以在后期轻松替换数据源,甚至添加离线优先等特性。
效果与收益总结
这次架构重构带来的好处非常明显:
1. 可维护性大幅提升
以前改个页面逻辑动辄改动多个 VC 文件,现在基本上集中在 ViewModel 和 Repository 中完成。代码结构更清晰,修改风险更小。
2. 开发效率稳中有升
虽然初期大家都觉得“麻烦”,但一旦习惯新架构之后,开发效率反而提升了。特别是新同学更容易接手项目,因为所有逻辑都有清晰的路径。
3. 复用能力增强
不同页面间共享 ViewModel 和 Repository 成为可能。例如我们把“点赞”这个功能封装成独立的组件,多个页面都可以快速接入。
4. 易于测试
有了 ViewModel 和 Repository 分离后,单元测试覆盖率可以从原来的 20% 提升到 60% 以上。这对长期质量保障至关重要。
我的经验与建议
如果你也在考虑进行类似的架构升级或者技术探索,以下几点是我从实践中提炼出来的建议:
1. 不追求完美,要追求可用性和落地性
不要一开始就想着搞一套“终极架构”。技术选型要考虑团队成员的接受程度和迁移成本。有时候,简单的 MVVM 就比复杂的 VIPER 更有效。
2. 边重构边验证,不要闷头大改
我们采取的做法是:先选一个小页面试点,再逐步推广。每一步都要有明确的验收标准和回滚预案,避免陷入“永远改不完”的泥潭。
3. 文档和培训同样重要
技术变革不是写几篇文章就能解决的。一定要配套培训、代码模板、最佳实践案例,最好能有一两个样板工程作为参考。
4. 不要把架构当万能钥匙
再好的架构也不能解决所有问题。有时候业务本身复杂度过高,单纯靠架构也无能为力。在这种情况下,应该配合业务层面的拆解优化。
5. 关注技术趋势,但也要结合实际
我们现在也在研究 SwiftUI + Composable Architecture 的可能性,但在生产环境大规模使用之前,会谨慎评估它的适用性。新技术总是有红利,但要用对场景。
写在最后
作为一名 iOS 开发者,我一直认为,技术探索的最终目的是服务业务,而不是炫技。每一次架构升级的背后,其实都是无数次的权衡与取舍。
在这次重构过程中,我也犯过不少错误,走过不少弯路,但正是这些试错的过程让我成长了很多。技术探索从来都不是一条笔直的道路,它更像是在一个迷宫中不断寻找最优解。
希望我的这段经历对你有所启发。愿我们都能在日常工作中坚持技术思考,在实践中打磨自己的工程能力。
如果你也有类似的技术改造经历,欢迎留言交流,我们一起探讨更多实战经验和架构心得!

评论 0