【转】CODE WITH ANDREA:使用 Riverpod 的 Flutter 应用架构介绍

2024/08/16 Flutter 共 2989 字,约 9 分钟
Bob.Zhu

当构建复杂应用当时候,选择正确的应用架构至关重要,因为它能够帮助你组织代码结构,并在迭代开发之后代码量变大的时候提供良好的支持。 良好的架构应该能帮助您处理复杂性而不会造成阻碍。但要做到这一点并不容易:

  • “太少” 的架构导致代码组织混乱,缺乏明确的约定,
  • “过多” 会导致过度工程,甚至很难做出简单的改变。

在实践中,事情可能非常微妙,取得正确的平衡可能很棘手。 因此,在本文中,我将分享一个新的参考架构,您可以使用它来:

  • 确保你的UI 代码、业务逻辑和数据访问逻辑之间的关注点得到良好的分离
  • 使用最少的样板代码轻松获取和缓存数据
  • 以可预测的方式处理 UI 状态(数据、加载、错误)时执行数据变更
  • 轻松编写可测试的代码并模拟依赖关系

该架构严重依赖 Riverpod 包,充分利用其反应式缓存和数据绑定功能。 但是我们为什么首先需要参考架构呢?

Riverpod 强制规范不足

Riverpod 很棒,我在我的所有应用中都使用它, 并且写了很多关于它的教程 - 但官方文档没有提供任何关于如何拆分、组织、管理你的代码的指导。

因此,您需要独自做出以下重要决定:

  • 如何将 UI 代码与业务逻辑和数据访问逻辑分开?
  • 你应该写哪些类?它们的职责是什么?它们如何相互通信?
  • 如何让你的代码更具可扩展性——以便不同的团队成员可以独立处理多个功能?
  • 您应该如何组织您的文件?
  • 那么错误处理呢?它应该在哪里进行?错误应该如何传递到 UI?
  • 您应该使用哪些Provider?以及应该在哪里声明它们?

这些决定很重要。如果你听之任之,最终会遇到性能问题、错误和持续的维护问题,从而影响你的开发速度。

这就是拥有一个严格规范的应用程序架构所带来的不同之处。👇

推荐的应用程序架构

在构建不同复杂程度的 Flutter 应用程序时,我对应用程序架构进行了大量实验,并深入了解了哪些有效、哪些无效, 然后就产生了这个 参考架构,我在所有最新项目中都使用了它。

由于缺乏更好的名称,我将其称为 Riverpod 架构 - 但请记住,这只是我的看法,而不是 Remi Rousselet(Riverpod 的作者)认可的“官方”架构。

该架构包含四层(数据层、 领域层、 应用层、展示层),以下是总览: flutter-app-architecture.webp @2x

每一层都有各自的职责,并且对于如何跨越边界进行沟通有着明确的约定。

展示层:The Presentation Layer

这通常被称为 UI 层。这篇关于Android 应用架构的指南 对此进行了很好的描述:

UI 的作用是将应用数据显示在屏幕上,同时也是用户交互的主要点。 每当数据发生变化时,无论是由于用户交互(如按下按钮)还是外部输入(如网络响应),UI 都会更新以反映这些变化。 实际上,UI 是从数据层检索到的应用状态的视觉表示。

在我们的架构中,展示层包含两种主要类型的组件:

  • Widget:是要在屏幕上显示的数据的表示。
  • Controller:执行异步数据变更并管理Widget状态。

presentation-layer-standalone @2x

Controller 通常是 AsyncNotifier 的子类,我在这篇单独的文章中详细介绍了它们: Flutter 应用架构:展示层

领域层:The Domain Layer

领域层的主要作用是定义适用于应用程序的模型类(Model),接收来自数据层的数据。 模型类是简单的数据类,具有以下要求:

  • 它们是永远不变的。
  • 它们包含序列化逻辑(例如fromJsontoJson方法)。
  • 它们实现了==操作符和hashCode方法。

以电子商务应用程序为例,我们可以识别以下实体并将它们表示为模型类:

ecommerce-entities @2x

模型类不关心从哪里获取数据,也没有其他依赖项。 最多可能依赖于其他模型类(例如,一个ShoppingCart类可能包含Products列表)。 因此,模型类可以导入并在应用程序的其他地方使用(Widget, Controller, Service, Repository)。

为了更轻松地定义数据类中的属性和方法,您可以使用FreezedEquatable等包, 或Dart 数据类生成器等工具。

要了解有关域层和模型类的更多信息,请阅读以下内容: Flutter 应用架构:领域层

数据层:The Data Layer

数据层包含三种类型的类:

  • Data Source:用于与外界通信的第三方 API(例如远程数据库、REST API 客户端、推送通知系统、蓝牙接口)。
  • Data Transfer Object:或者DTO,由数据源返回。通过网络发送数据时,DTO 通常表示为非结构化数据(例如 JSON)
  • Repository:用于从各种来源(例如后端 API)访问 DTO,并使其作为类型安全的模型类(又名实体)可供应用程序的其余部分使用。

data-layer-standalone @2x

请注意,数据源和 DTO 都是外部依赖项。如果要使用它们,需要将包导入到您的应用后使用它们的 API。 其他的 Repository 是自己项目中定义的类,需要自己设计并实现相关API。

如果您的 Flutter 应用与本地或远程数据库通信,则该数据库是您数据的唯一真实来源。 就您的应用而言,Repository 是通向该真实来源的门户。 此应用架构通过实现从数据层一直到 UI 的单向数据流来解决此问题。

要了解有关 Repository 和数据层的更多信息,请阅读以下内容:Flutter 应用架构:数据层

应用层:The Application Layer

在构建复杂的应用程序时,可能需要实现如下逻辑:

  • 依赖于多个数据源或 Repository
  • 需要被多个 Widget 使用(共享)

在这种情况下,我们很容易将该逻辑放入我们已经拥有的类(Controller或Repository)中。

但这会导致关注点分离不佳,使我们的代码更难阅读、维护和测试。

为了解决这个问题,我们可以引入一个新的可选层,称为应用层。在其中,我们可以添加Service类, 它们充当Controller(仅管理Widget状态)和Repository(与不同数据源通信)之间的中间人。

下面是一个示例,展示了CartService在 Controller 和 Repository 之间进行通信: shopping-cart-layers @2x

要了解有关应用层的更多信息,请阅读:Flutter 应用架构:应用层

整合

到目前为止,我们已经了解了我的 Riverpod架构 中的四个主要层及其内部的类(Widget、Controller、model、Service、Repository、数据源)。

但是所有这些不同的类如何相互作用,以及我们如何使用它们在我们的应用程序中构建工作功能?

这就是 Riverpod 及其所有有用的提供程序发挥作用的地方。在构建移动应用程序时,我们所做的大部分工作可以归结为两件事:

  • 获取数据并在 UI 中显示
  • 响应输入事件执行数据变更

要了解更多信息,您可以阅读以下后续文章:如何使用 Riverpod 架构获取数据并执行数据变更

参考资料

文档信息

Search

    Table of Contents