MVC 是一种按职责组织代码和服务的架构思想。三个字母分别代表 Model、View 和 Controller。它不是某个框架,也不要求项目中必须存在三个同名文件夹;关键是让展示、流程控制和数据状态各有明确归属。
三层分别做什么
| 层 | 可以把它理解为 | 负责 | 不负责 |
|---|
| View(视图) | 用户看见和操作的界面 | 渲染内容、收集点击和输入、展示加载或错误状态 | 决定权限、直接操作数据库 |
| Controller(控制器) | 请求的指挥者 | 接收请求、校验权限、安排调用顺序、组合结果 | 长期保存业务数据 |
| Model(模型) | 数据和业务状态的所有者 | 定义实体、查询和修改数据、通过仓储接口持久化 | 渲染界面、编排整个用户流程 |
最简短的记忆方式是:View 负责显示,Controller 负责协调,Model 负责数据。
一个具体例子
假设用户点击“收藏句子”:
- Flutter 页面接收点击并发送收藏请求,这是 View 的工作。
- 服务端识别当前用户、检查请求是否合法,并决定调用哪个数据服务,这是 Controller 的工作。
- 收藏服务创建收藏记录并写入数据库,这是 Model 的工作。
- 结果沿原路返回,View 再更新按钮状态。
如果应用重启后收藏仍然存在,是因为状态由 Model 持久化了,而不是由页面或 Controller 暂存。
在 RakuLLApp 中如何对应
| MVC 角色 | RakuLLApp 组件 |
|---|
| View | rakullapp_core、rakull_manager |
| Controller | immersive_study_server、manager_server |
| Model | data_server、user_manager、collection_server |
| MVC 之外的计算黑盒 | agent_server |
agent_server 负责 AI 计算,但不拥有计算结果。Controller 调用它取得结果后,再交给相应的 Model 保存。因此即使替换 Agent,已经保存的文章、句子和讲解仍属于 Model。
RakuLLApp 的 MVC 是服务级职责划分,不是传统单体 Web 框架中一个请求严格经过三个类的写法。
为什么要这样分层
- 容易定位修改位置:界面问题看 View,流程和权限看 Controller,数据问题看 Model。
- 降低耦合:更换 Flutter 页面或 AI 实现时,不必迁移业务数据。
- 更容易测试:Controller 可以用假的下游服务测试编排;Model 可以独立测试读写。
- 避免状态混乱:同一种实体只有一个明确的所有者,不会在多个服务中各存一份真相。
常见误解
Model 不只是数据库表
Model 还包括实体约束、仓储接口和数据读写规则。数据库只是它实现持久化的一种方式。
Controller 不是“所有业务代码”
Controller 负责一次请求的流程和权限,但实体自身的数据规则应留在拥有该实体的 Model 中。
View 可以有界面状态
输入框内容、当前选中的标签页、加载动画等短期 UI 状态可以留在 View。不能放在 View 的是文章、收藏、用户权限等需要长期保存或跨客户端一致的业务状态。
接下来可阅读 架构总览,了解这些职责在实际服务中的完整分布。