架构案例研究
本指南中的代码示例来自 Compass 示例应用,该应用帮助用户构建和预订行程。这是一个功能强大的示例应用,包含众多功能、路由和屏幕。该应用与 HTTP 服务器通信,具备开发和生产环境,包含品牌专属样式,并拥有高测试覆盖率。通过这些方式以及更多特性,它模拟了一个真实世界中功能丰富的 Flutter 应用。




Compass 应用的架构最类似于 MVVM 架构模式,如 Flutter 的 应用架构指南 中所述。本架构案例研究通过 Compass 应用的“Home”功能,演示了如何实现这些指南。如果您不熟悉 MVVM,应首先阅读这些指南。
Compass 应用的“Home”主屏幕显示用户账户信息和用户已保存的行程列表。从该屏幕,您可以退出登录、打开详细行程页面、删除已保存行程,并导航到核心应用流程的第一页,该页面允许用户创建新的行程。
在本案例研究中,您将学到以下内容:
- 如何使用数据层中的仓库(repositories)和服务(services)以及UI 层中的 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_model
│ │ └─── <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
// The test folder contains unit and widget tests
test
├─── data
├─── domain
├─── ui
└─── utils
// The testing folder contains mocks other classes need to execute tests
testing
├─── fakes
└─── models
大部分应用代码位于 data
、domain
和 ui
文件夹中。data
文件夹按类型组织代码,因为仓库和服务可以在不同功能和多个视图模型之间共享使用。ui
文件夹按功能组织代码,因为每个功能都只有一个视图和一个视图模型。
此文件夹结构的其他显著特点:
- UI 文件夹还包含一个名为“core”的子目录。Core 包含多个视图共享的组件(widgets)和主题逻辑,例如带有品牌样式的按钮。
domain
文件夹包含应用的数据类型,因为它们被数据层和 UI 层使用。- 该应用包含三个“main”文件,它们作为应用在开发、预发布和生产环境中的不同入口点。
- 在与
lib
同级的位置,有两个与测试相关的目录:test/
存放测试代码,其结构与lib/
匹配。testing/
是一个子包,包含模拟对象(mocks)和其他测试工具,可在其他包的测试代码中使用。testing/
文件夹可以被描述为您的应用中不随产品发布的部分。它是被测试的内容。
Compass 应用中还有其他不属于架构相关的代码。要查看完整的包结构,请在 GitHub 上查看。
其他架构选项
#本案例研究中的示例演示了一个应用如何遵循我们推荐的架构规则,但还有许多其他示例应用也可以用不同的方式编写。此应用的 UI 大量依赖于视图模型和 ChangeNotifier
,但它也可以很容易地使用流(streams)或 riverpod
、flutter_bloc
和 signals
等包提供的其他库来编写。此应用层之间的通信全部通过方法调用处理,包括轮询新数据。它也可以使用流来从仓库向视图模型公开数据,并且仍然符合本指南中涵盖的规则。
即使您严格遵循本指南,并选择不引入额外的库,您仍需要做出决定:您会有一个领域层(domain layer)吗?如果会,您将如何管理数据访问?答案很大程度上取决于各个团队的需求,因此没有唯一正确的答案。无论您如何回答这些问题,本指南中的原则都将帮助您编写可扩展的 Flutter 应用。
如果您仔细看,难道所有架构最终不都是 MVVM 吗?
反馈
#由于本网站的此部分正在不断完善中,我们欢迎您提供反馈!