跳到主内容

常见的架构概念

了解应用程序设计中的常见架构概念,以及它们如何应用于 Flutter。

在本节中,您将找到在更广泛的应用程序开发领域中经过实践检验的原则,以及有关它们如何与 Flutter 具体契合的信息。这是一个对推荐架构和最佳实践相关术语和概念的温和介绍,以便可以在本指南的其余部分中更详细地探讨它们。

关注点分离

#

关注点分离是应用程序开发中的一个核心原则,它通过将应用程序的功能划分为不同的、自包含的单元来促进模块化和可维护性。从高层来看,这意味着将您的 UI 逻辑与业务逻辑分离。这通常被描述为分层架构。在每一层中,您应该通过功能或特性进一步分离您的应用程序。例如,您的应用程序的身份验证逻辑应该在与搜索逻辑不同的类中。

在 Flutter 中,这适用于小部件 (widgets)WidgetFlutter 用户界面的基本构建块。了解更多在 UI 层中也是如此。您应该编写可重用、精简的小部件,这些小部件包含尽可能少的逻辑。

分层架构

#

Flutter 应用程序应该以的形式编写。分层架构是一种软件设计模式,它将应用程序组织成不同的层,每一层都有特定的角色和职责。通常,应用程序根据复杂程度分为 2 到 3 层。

The three common layers of app architecture, the UI layer, logic layer, and data layer.
  • UI 层 - 显示业务逻辑层暴露给用户的数据,并处理用户交互。这通常也称为“表示层”。
  • 逻辑层 - 实现核心业务逻辑,并促进数据层和 UI 层之间的交互。通常被称为“领域层”。逻辑层是可选的,只有当您的应用程序具有在客户端上发生的复杂业务逻辑时才需要实现它。许多应用程序只关心向用户呈现数据并允许用户更改该数据(通俗地称为 CRUD 应用程序)。这些应用程序可能不需要这个可选层。
  • 数据层 - 管理与数据源的交互,例如数据库或平台插件。向业务逻辑层公开数据和方法。

这些被称为“层”,因为每一层只能与直接上下层的层进行通信。UI 层不知道数据层是否存在,反之亦然。

单一数据源 (Single source of truth)

#

您的应用程序中的每种数据类型都应该有一个单一数据源 (SSOT)。数据源负责表示本地或远程状态。如果数据可以在应用程序中修改,则 SSOT 类应该是唯一可以执行此操作的类。

这可以大大减少应用程序中的错误数量,并且可以简化代码,因为您将只拥有相同数据的唯一副本。

通常,应用程序中任何给定类型数据的源头都保存在一个名为 Repository (仓库) 的类中,该类是数据层的一部分。通常,对于应用程序中的每种类型的数据,都有一个仓库类。

这个原则也可以应用于应用程序中的各个层和组件,以及单个类中。例如,一个 Dart 类可以使用 getter 从 SSOT 字段派生值(而不是拥有需要独立更新的多个字段),或者使用 records (记录) 来分组相关值(而不是索引可能不同步的并行列表)。

单向数据流

#

单向数据流 (UDF) 指的是一种设计模式,有助于将状态与显示该状态的 UI 解耦。最简单地说,状态从数据层流经逻辑层,最终流向 UI 层的 widgets。来自用户交互的事件反向流动,从表示层流回逻辑层,再到数据层。

The three common layers of app architecture, the UI layer, logic layer, and data layer, and the flow of state from the data layer to the UI layer.

在 UDF 中,从用户交互到重新渲染 UI 的更新循环如下

  1. [UI 层] 由于用户交互(例如单击按钮)而发生事件。小部件的事件处理程序回调调用逻辑层中的类公开的方法。
  2. [逻辑层] 逻辑类调用仓库公开的方法,这些方法知道如何修改数据。
  3. [数据层] 仓库更新数据(如果需要),然后将新数据提供给逻辑类。
  4. [逻辑层] 逻辑类保存其新状态,并将其发送到 UI。
  5. [UI 层] UI 显示视图模型的新状态。

新数据也可以从数据层开始。例如,仓库可能会轮询 HTTP 服务器以获取新数据。在这种情况下,数据流仅进行第二阶段的旅程。最重要的是,数据更改始终发生在 SSOT 中,即数据层。这使您的代码更易于理解、更不易出错,并防止创建格式错误或意外的数据。

UI 是 (不可变) 状态的函数

#

Flutter 是声明式的,这意味着它构建 UI 以反映应用程序的当前状态。当状态更改时,您的应用程序应该触发依赖于该状态的 UI 的重建。在 Flutter 中,您经常听到这种描述为“UI 是状态的函数”。

UI is a function of state.

至关重要的是,您的数据驱动您的 UI,而不是相反。数据应该是不可变的和持久的,并且视图应该包含尽可能少的逻辑。这最大限度地减少了应用程序关闭时数据丢失的可能性,并使您的应用程序更易于测试且不易出错。

可扩展性

#

每个架构部分都应该有一个明确定义的输入和输出列表。例如,逻辑层中的视图模型应该只将数据源作为输入(例如仓库),并且应该只公开为视图格式化的命令和数据。

以这种方式使用干净的接口允许您在无需更改使用该接口的代码的情况下交换类的具体实现。

可测试性

#

使软件可扩展的原则也使软件更易于测试。例如,您可以测试视图模型的自包含逻辑,方法是模拟仓库。视图模型测试不需要您模拟应用程序的其他部分,并且您可以将 UI 逻辑与 Flutter widgets 本身分开进行测试。

您的应用程序也将更加灵活。添加新逻辑和新 UI 将会很直接且风险很低。例如,添加新的视图模型不会破坏数据或业务逻辑层的任何逻辑。

下一节解释了应用程序架构中任何给定组件的输入和输出的概念。

反馈

#

由于本网站的这一部分正在不断发展,我们欢迎您的反馈