现代化Flutter架构-Riverpod简介

814 阅读6分钟

在构建复杂的应用程序时,选择正确的应用程序架构至关重要,因为它可以让您构建代码,并在代码库增长时为其提供支持。

良好的架构应能帮助您处理复杂性,而不会碍手碍脚。但要做到这一点并不容易:

  • 架构 “不足 ”,会导致代码条理不清,缺乏明确的规范
  • 架构 “过多 ”,会导致过度工程化,即使是简单的更改也很难进行

在实践中,事情可能会有很多细微差别,要取得正确的平衡也很棘手。

因此,在本文中,我将分享一个新的参考架构,您可以用来:

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

这种架构主要依赖于 Riverpod 软件包,充分利用了其反应式缓存和数据绑定功能。

但为什么我们首先需要一个参考架构呢?

Riverpod没有官方指导

Riverpod很棒。我在我所有的应用程序中都使用了它,并撰写了许多关于它的教程--但官方文档并未就如何构建代码提供任何指导。

因此,在做出以下重要决定时,你只能靠自己:

  • 如何将用户界面代码与业务逻辑和数据访问逻辑分开?
  • 应该编写哪些类?它们的职责是什么?它们之间如何通信?
  • 如何使代码更具可扩展性--以便不同的团队成员可以独立完成多个功能?
  • 如何组织文件?
  • 如何处理错误?应该在哪里处理,错误应该如何传播到用户界面?
  • 应该使用哪些提供程序,在哪里声明?

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

因此,拥有一个优秀的应用程序架构就显得尤为重要。👇

Riverpod + Reference架构 = 👌

在构建不同复杂度的 Flutter 应用程序时,我对应用程序架构进行了大量尝试,并对哪些有效、哪些无效有了深刻的理解。

因此,我在所有最新项目中都使用了Reference架构。

由于没有更好的名字,我称之为 “Riverpod 架构”--不过请记住,这只是我的想法,并不是 Remi Rousselet(Riverpod 的作者)认可的 “官方 ”架构。

该架构由四层组成(数据层、领域层、应用层和表现层)。下面是预览:

每一层都有自己的职责,并且有明确的合同规定如何进行跨边界沟通。

让我们来详细了解一下每一层。👇

由于需要涉及的内容很多,本文只对这四个层次进行了概念性、高层次的概述。下面是更详细介绍各层的独立文章链接。

表现层

通常称为用户界面层。这本关于 Android 应用程序架构的指南对其进行了很好的描述:

用户界面的作用是在屏幕上显示应用程序数据,同时也是用户交互的主要场所。每当数据发生变化时,无论是由于用户交互(如按下按钮)还是外部输入(如网络响应),用户界面都应更新以反映这些变化。实际上,用户界面就是从数据层获取的应用程序状态的可视化表示。

在我们的架构中,表现层主要包含两类组件:

  • Widgets,它是要显示在屏幕上的数据的展示
  • Controllers,用于执行异步数据转换和管理 Widget 状态

Controllers本身通常表示为 AsyncNotifier 子类。

领域层

领域层的主要作用是定义特定于应用程序的模型类,用于表示来自数据层的数据。

模型类是简单的数据类,有以下要求:

  • 它们总是不可变的。
  • 包含序列化逻辑(如 fromJson 和 toJson 方法)。
  • 实现 == 运算符和 hashCode 方法。

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

模型类可能依赖于其他模型类(例如,ShoppingCart 类可能包含一个产品列表)。但它们不知道从哪里获取数据,也没有其他依赖关系。因此,模型类可以导入并用于应用程序中的其他任何地方(widgets, controllers, services, repositories)。

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

数据层

数据层包含三种类型的类:
  • 数据源,即用于与外部世界通信的第三方 API(如远程数据库、REST API 客户端、推送通知系统、蓝牙接口)。
  • 数据传输对象(或 DTO),由数据源返回。通过网络发送数据时,DTO 通常表示为非结构化数据(如 JSON)。
  • Repository,用于从各种来源(如后端 API)访问 DTO,并将其作为类型安全的模型类(又称实体)提供给应用程序的其他部分。

请注意,数据源和 DTO 都是外部依赖项。要使用它们,您需要在应用程序中导入软件包并使用它们的 API。

另一方面,Repository是您代码库中的类,因此实现它们并设计其 API 是您的工作。

如果您的 Flutter 应用程序与本地或远程数据库通信,那么该数据库就是您数据的唯一来源。就您的应用程序而言,Repository是通往真实数据源的网关。此应用程序架构通过实现从数据层到用户界面的单向数据流来实现这一点。

应用层

在构建复杂应用程序时,我们可能会发现自己编写的逻辑需要:
  • 依赖于多个数据源或Repository
  • 需要被多个Widget使用或者共享

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

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

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

下面的示例展示了一个在Controller和Repository之间起中介作用的 CartService 类:

本文翻译自:codewithandrea.com/articles/fl…

欢迎大家关注我的公众号——【群英传】,专注于「Android」「Flutter」「Kotlin」

我的语雀知识库——www.yuque.com/xuyisheng