跳到主内容

架构案例研究

通过一个实现 MVVM 架构模式的 Flutter 应用进行详细剖析。

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

A screenshot of the splash screen of the compass app.
A screenshot of the home screen of the compass app.
A screenshot of the search form screen of the compass app.
A screenshot of the booking screen of the compass app.

Compass 应用的架构最接近 Flutter 应用架构指南中所述的 MVVM 架构模式。本架构案例研究通过剖析 Compass 应用的“首页”功能,演示了如何实现这些指南。如果你不熟悉 MVVM,建议先阅读这些指南。

Compass 应用的首页显示了用户账户信息和已保存的旅行列表。在此屏幕上,你可以注销、打开详细的旅行页面、删除已保存的旅行,以及导航到核心应用流程的第一页,从而允许用户构建新的行程。

在本案例研究中,你将学习以下内容:

本案例研究按顺序阅读编写。任何页面都可能引用前面的页面。

本案例研究中的代码示例包含了理解架构所需的所有细节,但它们不是完整的、可运行的代码片段。如果你更喜欢跟随完整的应用学习,可以在 GitHub 上找到它。

包结构

#

组织良好的代码更便于多位工程师协作,减少代码冲突,也更容易让新加入的工程师理解和上手。良好的代码组织既得益于定义明确的架构,也能促进架构的完善。

组织代码有两种流行方式:

  1. 按功能划分 - 将每个功能所需的类归为一组。例如,你可能有一个 auth 目录,其中包含 auth_viewmodel.dartlogin_usecase.dartlogout_usecase.dartlogin_screen.dartlogout_button.dart 等文件。
  2. 按类型划分 - 将每种“类型”的架构组件归为一组。例如,你可能拥有 repositoriesmodelsservicesviewmodels 等目录。

本指南推荐的架构结合了这两种方式。数据层对象(仓库和服务)不绑定到单个功能,而 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/

大部分应用代码位于 datadomainui 文件夹中。Data 文件夹按类型组织代码,因为仓库和服务可以在不同的功能之间被多个视图模型共享。Ui 文件夹按功能组织代码,因为每个功能恰好有一个视图和一个视图模型。

此文件夹结构的其他显著特点:

  • UI 文件夹还包含一个名为“core”的子目录。Core 包含由多个视图共享的组件和主题逻辑,例如带有品牌样式的按钮。
  • Domain 文件夹包含应用数据类型,因为它们被数据层和 UI 层共同使用。
  • 该应用包含三个“main”文件,分别作为开发、预发布(staging)和生产环境的应用入口点。
  • lib 同级目录下有两个与测试相关的目录:test/ 包含测试代码,其结构与 lib/ 对应。testing/ 是一个子包,包含可用于其他包测试代码的模拟对象和其他测试工具。testing/ 文件夹可以描述为你不发布的那部分应用内容,也就是被测试的内容。

Compass 应用中还有一些与架构无关的附加代码。如需查看完整的包结构,请在 GitHub 上查看

其他架构选项

#

本案例研究中的示例演示了一个应用如何遵循我们推荐的架构规则,但实际上还有许多其他编写应用的方式。此应用的 UI 严重依赖于视图模型和 ChangeNotifier,但它完全可以用流(streams)或使用其他库(如 riverpodflutter_blocsignals)来编写。此应用各层之间的通信全权通过方法调用处理,包括轮询新数据。它也可以改用流来将数据从仓库暴露给视图模型,同时依然遵循本指南所涵盖的规则。

即使你完全遵循本指南且不引入额外的库,你仍然需要做出抉择:是否需要领域层(domain layer)?如果需要,如何管理数据访问?答案很大程度上取决于团队的具体需求,因此没有唯一的标准答案。无论你如何回答这些问题,本指南中的原则都将帮助你编写可扩展的 Flutter 应用。

如果仔细观察,其实所有架构本质上都是 MVVM,不是吗?

反馈

#

随着网站该部分的完善,我们欢迎你的反馈