6-6.【架构设计】如果要在一个老 MVC 项目中逐步引入 TCA / MVVM,从哪里切?说一个不会导致全盘重构失败的切入策略。

2 阅读2分钟

一、原则:逐步替换、屏蔽风险

  1. 不要一次性改所有 ViewController → 小 Feature 或屏幕级别切
  2. 保持 MVC 原有功能可运行 → MVVM / TCA 先是局部替代
  3. State/Action/Binding 只替换单一 Feature → 避免跨模块依赖链断裂
  4. 数据流与副作用隔离 → MVVM/TCA 的逻辑尽量不破坏现有 Model/Service

二、切入策略(一步步操作)

1️⃣ 选择低风险、独立 Feature

  • 选择依赖少、逻辑简单、UI 独立的模块

  • 例子:

    • 单页表单
    • 列表 + 搜索
    • 单独的 Profile 页面
  • 避免:AppDelegate/TabBar/复杂导航页作为首切


2️⃣ 从 ViewModel 包裹 Model 开始(MVVM)

  • 保持原有 MVC 运行:

    • VC 仍然存在,但可以把数据逻辑迁移到 ViewModel
  • 操作方式:

// Step 1: 只包裹 Model
class UserViewModel {
    private let user: User
    var displayName: String { user.name.uppercased() }
}

// Step 2: VC 依赖 ViewModel 而非 Model
class UserViewController: UIViewController {
    var viewModel: UserViewModel!
    @IBOutlet weak var nameLabel: UILabel!

    func refreshUI() {
        nameLabel.text = viewModel.displayName
    }
}

✅ 结果:

  • VC 没有业务逻辑
  • 不破坏现有导航和生命周期
  • 完全可以在老项目中插入

3️⃣ 逐步引入 TCA / 单向数据流

  • 从单独 Feature Store 开始,不要改全局 App State
  • State/Action/Reducer 只针对这个 Feature
  • Effect 可以先调用现有 Service / API,不用重写全部业务逻辑

示意:

struct CounterState { var count: Int = 0 }
enum CounterAction { case increment, decrement }
let counterReducer = Reducer<CounterState, CounterAction, Void> { state, action, _ in
    switch action {
    case .increment: state.count += 1
    case .decrement: state.count -= 1
    }
    return .none
}

struct CounterView: View {
    let store: Store<CounterState, CounterAction>
    var body: some View { /* 用 ViewStore 绑定 */ }
}

重点:不破坏原有 MVC,只在新 View/Feature 使用 TCA


4️⃣ 保持桥接(兼容老代码)

  • ViewController 可以作为 TCA 的 Host:

    • 老 VC → 包裹 TCA View
    • 新 Feature → 直接 SwiftUI + TCA
  • 数据交互用 协议或闭包桥接

    • 避免老 MVC 直接操作新 Store
    • 保持单向数据流

5️⃣ 迭代替换

  1. 新功能 → 全 MVVM / TCA
  2. 老 VC 的局部逻辑迁移 → 先 ViewModel,再逐步引 TCA
  3. 随 Feature 更新 → State / Action / Effect 扩展
  4. 最终可把全局 AppState 逐步整合,老 MVC 可以安全下线

三、实战要点

  1. 不要试图一次改全局 AppState / 全局 TCA Store

  2. ViewModel 优先,再升级为 TCA

  3. 保留桥接接口

    • 协议 / Delegate / closure → 保持 MVC 与 MVVM/TCA 交互
  4. 单 Feature 迭代

    • 列表页、表单页 → 先改
    • TabBar / Root VC → 最后再考虑
  5. 测试优先

    • MVVM / TCA 逻辑可单元测试
    • 老 MVC 依然可以运行,保证迭代安全

四、一句话总结切入策略

先从独立 Feature 的 ViewModel 开始,把业务逻辑从 VC 拆出,逐步引入 TCA 单向数据流,通过协议/桥接保持 MVC 兼容,最后再迭代替换全局状态。