架构案例研究
通过一个实现 MVVM 架构模式的 Flutter 应用进行详细剖析。
本指南中的代码示例来自 Compass 示例应用,该应用旨在帮助用户构建和预订旅行行程。这是一个功能完备的示例应用,拥有丰富的功能、路由和屏幕。该应用与 HTTP 服务器通信,具备开发和生产环境,包含品牌特定的样式,并拥有较高的测试覆盖率。通过这些方式,它模拟了一个真实的、功能丰富的 Flutter 应用。




Compass 应用的架构最接近 Flutter 应用架构指南中所述的 MVVM 架构模式。本架构案例研究通过剖析 Compass 应用的“首页”功能,演示了如何实现这些指南。如果你不熟悉 MVVM,建议先阅读这些指南。
Compass 应用的首页显示了用户账户信息和已保存的旅行列表。在此屏幕上,你可以注销、打开详细的旅行页面、删除已保存的旅行,以及导航到核心应用流程的第一页,从而允许用户构建新的行程。
在本案例研究中,你将学习以下内容:
- 如何通过在数据层使用仓库(Repository)和服务(Service),以及在界面层使用 MVVM 架构模式来实现 Flutter 的应用架构指南。
- 如何使用命令模式(Command pattern)在数据变化时安全地渲染 UI。
- 如何使用
ChangeNotifier和Listenable对象来管理状态。 - 如何使用
package:provider实现依赖注入(Dependency Injection)。 - 如何在遵循推荐架构的情况下设置测试。
- 针对大型 Flutter 应用的有效包结构。
本案例研究按顺序阅读编写。任何页面都可能引用前面的页面。
本案例研究中的代码示例包含了理解架构所需的所有细节,但它们不是完整的、可运行的代码片段。如果你更喜欢跟随完整的应用学习,可以在 GitHub 上找到它。
包结构
#组织良好的代码更便于多位工程师协作,减少代码冲突,也更容易让新加入的工程师理解和上手。良好的代码组织既得益于定义明确的架构,也能促进架构的完善。
组织代码有两种流行方式:
- 按功能划分 - 将每个功能所需的类归为一组。例如,你可能有一个
auth目录,其中包含auth_viewmodel.dart、login_usecase.dart、logout_usecase.dart、login_screen.dart、logout_button.dart等文件。 - 按类型划分 - 将每种“类型”的架构组件归为一组。例如,你可能拥有
repositories、models、services和viewmodels等目录。
本指南推荐的架构结合了这两种方式。数据层对象(仓库和服务)不绑定到单个功能,而 UI 层对象(视图和视图模型)则绑定到特定功能。以下是 Compass 应用中代码的组织方式。
-
lib/
ui/
core/
ui/
- <shared_widgets>
- themes/
<feature_name>/
view_models/
- <view_model_class>.dart
widgets/
- <feature_name>_screen.dart
- <other_widgets>
domain/
models/
- <model_name>.dart
data/
repositories/
- <repository_class>.dart
services/
- <service_class>.dart
model/
- <api_model_class>.dart
- config/
- utils/
- routing/
- main_staging.dart
- main_development.dart
- main.dart
-
test/// 包含单元测试和组件测试。
- data/
- domain/
- ui/
- utils/
-
testing/// 包含其他类执行测试所需的模拟对象 (mocks)。
- fakes/
- models/
大部分应用代码位于 data、domain 和 ui 文件夹中。Data 文件夹按类型组织代码,因为仓库和服务可以在不同的功能之间被多个视图模型共享。Ui 文件夹按功能组织代码,因为每个功能恰好有一个视图和一个视图模型。
此文件夹结构的其他显著特点:
- UI 文件夹还包含一个名为“core”的子目录。Core 包含由多个视图共享的组件和主题逻辑,例如带有品牌样式的按钮。
- Domain 文件夹包含应用数据类型,因为它们被数据层和 UI 层共同使用。
- 该应用包含三个“main”文件,分别作为开发、预发布(staging)和生产环境的应用入口点。
- 在
lib同级目录下有两个与测试相关的目录:test/包含测试代码,其结构与lib/对应。testing/是一个子包,包含可用于其他包测试代码的模拟对象和其他测试工具。testing/文件夹可以描述为你不发布的那部分应用内容,也就是被测试的内容。
Compass 应用中还有一些与架构无关的附加代码。如需查看完整的包结构,请在 GitHub 上查看。
其他架构选项
#本案例研究中的示例演示了一个应用如何遵循我们推荐的架构规则,但实际上还有许多其他编写应用的方式。此应用的 UI 严重依赖于视图模型和 ChangeNotifier,但它完全可以用流(streams)或使用其他库(如 riverpod、flutter_bloc 和 signals)来编写。此应用各层之间的通信全权通过方法调用处理,包括轮询新数据。它也可以改用流来将数据从仓库暴露给视图模型,同时依然遵循本指南所涵盖的规则。
即使你完全遵循本指南且不引入额外的库,你仍然需要做出抉择:是否需要领域层(domain layer)?如果需要,如何管理数据访问?答案很大程度上取决于团队的具体需求,因此没有唯一的标准答案。无论你如何回答这些问题,本指南中的原则都将帮助你编写可扩展的 Flutter 应用。
如果仔细观察,其实所有架构本质上都是 MVVM,不是吗?
反馈
#随着网站该部分的完善,我们欢迎你的反馈!