跳转至正文

常见架构概念

了解应用设计中的常见架构概念及其在 Flutter 中的运用。

在本节中,你将找到指导更广泛应用开发领域架构决策的成熟原则,以及它们如何具体适用于 Flutter 的说明。这是对推荐架构与最佳实践相关术语与概念的温和入门,以便在本指南其余部分深入探讨。

关注点分离

#

关注点分离 是应用开发的核心原则,通过将应用功能划分为彼此独立、自包含的单元来提升模块化与可维护性。从宏观上看,这意味着将 UI 逻辑与业务逻辑分开,通常称为分层架构。在各层内部,还应按功能或特性进一步拆分应用。例如,应用的认证逻辑应与搜索逻辑放在不同的类中。

在 Flutter 中,这也适用于 UI 层中的 widgetWidgetThe basic building block of a Flutter user interface. Learn more。你应编写可复用、精简、尽可能少包含逻辑的 widget。

分层架构

#

Flutter 应用应按编写。分层架构是一种软件设计模式,将应用组织为职责明确的若干层。通常根据复杂度分为 2 到 3 层。

The three common layers of app architecture, the UI layer, logic layer, and data layer.
  • UI 层 — 向用户展示业务逻辑层暴露的数据,并处理用户交互。也常称为「展示层」。

  • 逻辑层 — 实现核心业务逻辑,并协调数据层与 UI 层之间的交互。常称为「领域层」。逻辑层是可选的,仅当应用在客户端有复杂业务逻辑时才需要实现。许多应用只关注向用户展示数据并允许用户修改数据(俗称 CRUD 应用),可能不需要这一可选层。

  • 数据层 — 管理与数据库、平台插件等数据源的交互,并向业务逻辑层暴露数据与方法。

之所以称为「层」,是因为每一层只能与紧邻的上下层通信。 UI 层不应知道数据层的存在,反之亦然。

单一数据源

#

应用中每种数据类型都应有 单一数据源(SSOT)。数据源负责表示本地或远程状态。若数据可在应用内修改,SSOT 类应是唯一能修改它的类。

这能显著减少应用中的 bug 数量,并简化代码,因为同一份数据只会有一个副本。

通常,应用中某类数据的单一数据源由数据层中称为 Repository(仓库) 的类持有。应用中每种数据类型通常对应一个仓库类。

该原则可应用于应用各层与各组件,也可应用于单个类内部。例如,Dart 类可用 getter 从 SSOT 字段派生值(而不是维护多个需独立更新的字段),或用 record 列表将相关值分组(而不是使用索引可能失步的并行列表)。

单向数据流

#

单向数据流(UDF)是一种有助于将状态与展示该状态的 UI 解耦的设计模式。简而言之,状态从数据层经逻辑层最终流向 UI 层中的 widget;用户交互事件则沿相反方向,从展示层经逻辑层回到数据层。

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 层] 因用户交互(如按钮点击)产生事件。widget 的事件处理回调调用逻辑层类暴露的方法。

  2. [逻辑层] 逻辑类调用仓库暴露的、知道如何变更数据的方法。

  3. [数据层] 仓库更新数据(如有必要),再将新数据提供给逻辑类。

  4. [逻辑层] 逻辑类保存新状态并发送给 UI。

  5. [UI 层] UI 展示 view model 的新状态。

新数据也可以从数据层发起。例如,仓库可能轮询 HTTP 服务器获取新数据。此时数据流只完成后半段旅程。最重要的是:数据变更始终发生在作为数据层的 SSOT 中。这使代码更易理解、更少出错,并避免产生畸形或意外数据。

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

#

Flutter 是声明式的,即根据应用当前状态构建 UI。当状态变化时,应用应触发依赖该状态的 UI 重建。在 Flutter 中,这常被表述为「UI 是状态的函数」。

UI is a function of state.

关键是让数据驱动 UI,而非相反。数据应不可变且持久,view 应尽可能少包含逻辑。这能尽量减少应用关闭时数据丢失的可能,并提升可测试性与抗 bug 能力。

可扩展性

#

架构的每一部分都应有明确定义的输入与输出列表。例如,逻辑层中的 view model 应仅以仓库等数据源为输入,并仅向 view 暴露为其格式化的命令与数据。

以这种方式使用清晰接口,可在不修改消费方代码的情况下替换类的具体实现。

可测试性

#

使软件可扩展的原则同样使软件更易测试。例如,可通过 mock 仓库测试 view model 的自包含逻辑。 view model 测试无需 mock 应用其他部分,且可将 UI 逻辑与 Flutter widget 本身分开测试。

应用也会更灵活,新增逻辑与 UI 将直接且低风险。例如,新增 view model 不会破坏数据层或业务逻辑层的任何逻辑。

下一节将说明应用架构中任意组件的输入与输出概念。

反馈

#

网站本节内容仍在完善中, 欢迎提供反馈