Flutter Clean Architecture [1]:概述和项目结构

2025-06-07

Flutter Clean Architecture [1]:概述和项目结构

这篇文章是 Flutter App 教程系列的第一部分!

在早期阶段,一些软件开发人员只想快速掌握新技能并创建实用功能;他们并不关心编写简洁的代码或遵循正确的架构来提升应用程序性能。在使用框架时,其他一些开发人员经常使用著名的 MVC 模式,该模式使我们能够将应用程序分为模型、视图和控制器。在开发简单的应用程序时,这并非问题,但当我们需要通过添加新功能、更新库或执行类似操作来扩展应用程序时,我们就会遇到许多问题,因为随着代码耦合度的提高,如果不对整个代码进行大量更改,对应用程序进行更改将变得更加困难。因此,在开始开发应用程序之前,必须首先设计合适的架构。


在设计应用程序架构时,必须遵循几个原则,包括:

  • SOLID:五个面向对象编程原则有助于提高 OOP 设计的可读性、适应性和可维护性。

  • KISS:一种设计原则,指出设计和/或系统应尽可能简单,以最大限度地提高用户接受度和互动性。

  • DRY:一种软件开发原则,代表“不要重复自己”,旨在减少代码重复,有利于抽象并避免冗余。

近年来,我们看到了各种各样实现上述原则的架构理念,例如:

  • 六边形架构:端口和适配器架构基于将应用程序分离为松散耦合组件的思想,以便将核心业务逻辑与外部关注点隔离。

  • 洋葱架构:这种架构由多个同心层组成,这些层连接到代表领域的核心。实际上,这种架构基于控制反转的原理。

  • 整洁架构:Bob 大叔的架构基于依赖倒置原则,定义了高级组件和低级组件之间的界限。此外,该架构试图将所有先前的架构整合成一个可操作的理念。

图片描述
因此,正如我们所见,所有这些架构的根本目标都是实现清晰的关注点分离。这是通过将软件划分为不同的层来实现的,通常包括一个业务规则层和一个接口层。此外,每种架构都建立了一个定义明确的系统,该系统具有以下特点:
1. 独立于框架。2
. 可测试。3
. 独立于 UI。4
. 独立于数据库。5
. 独立于任何外部机构。


在讨论了最流行的架构及其作用之后,我将基于 Bob 大叔的概念为每个 Flutter 应用程序构建一个架构,该概念包含多种架构理念。
下图展示了建议的架构,其中的各个层用圆圈表示:

图片描述

需要注意的是,这种架构不仅仅是一个可以复制粘贴到项目中的文件夹结构。它是一种将应用程序分层的概念,同时遵循 Bob 大叔的依赖规则,该规则规定:

内圈中的任何事物都无法了解外圈中的任何事物。

换句话说,外层循环中声明的任何变量、类或函数都不能被内层循环提及。

让我们首先将这个架构集成到 Flutter 项目中,因为理论只能带你走这么远,而实际交互才是你学到最多的东西。

图片描述

我们将要创建的项目

我们将创建一个天气应用,旨在可视化特定城市的天气。该应用有两个界面:一个用于显示城市天气详情,另一个用于添加新城市。该应用将包含最基本的功能,例如从 API 获取数据、将数据存储在本地数据库、处理错误等等:

天气城市详情屏幕:

图片描述

添加nes城市物品屏幕:

图片描述

Flutter Clean Architecture 和数据流

图片描述
如上图所示,我决定在本项目中使用 Provider 技术进行状态管理。然而,由于清晰架构与任何特定的状态管理方法无关,您可以自由选择符合您偏好和项目目标的其他策略。

上图还展示了每个用户触发事件的数据流。实际上,当用户与Widget交互时,我们会将其操作传递给Provider类,该类随后连接到useCase以检索操作的结果。之后,useCase 与Repository类通信,以便从远程或本地DataSource获取解决方案。

因此,为了开始实现这些层,让我们将项目划分为子文件夹。


项目组织

图片描述

我们在 lib 文件夹中创建两个子文件夹。第一个文件夹名为“ core ”,它包含所有共享组件和基础组件,以及依赖注入等核心功能的实现。第二个文件夹名为“ features ”,它包含应用程序的所有“功能”。例如,在我们的例子中,我们只有一个表示天气信息的功能,它包含两个接口:一个用于详细信息,另一个用于添加新城市。每个功能也将分为三层:数据层域层演示层实用程序层
实用程序文件夹并非每个功能都需要;只有当您拥有单独的功能实用程序(例如枚举、类、扩展函数等)时才需要它。

要素图层说明

- 推介会

图片描述

顾名思义,该层负责通过小部件向用户呈现数据。此外,该层还负责监听状态并连接到 Provider 类,后者会将所有工作委托给具体的用例。在大多数情况下,呈现层的主要任务是处理基本的输入验证、动画和用户交互。

应用于天气应用程序

图片描述

在我们的例子中,每个界面都会有两个子文件夹,一个用于存放视图文件,另一个用于存放其对应的小部件。此外,我们还包含了提供程序文件。

- 领域

图片描述
这是连接其他两层的粘合层。它将使用业务对象(实体)和用例来实现应用程序的业务逻辑。此外,每个用例都依赖于存储库来检索数据。

实体:封装企业级的高级业务规则。实体仅保存从“业务角度”来看有意义的字段。此外,实体是视图最终使用的结果,并且当外部发生更改时,它最不可能发生变化。此外,实体不要求具有与 Web 服务返回的字段相同的字段;您可以根据接口需求自定义字段。例如,如果调用模型返回的是时间戳日期格式,则实体必须包含将被解析的转换后的日期。

用例:正如 Bob 大叔所说,用例“协调数据与实体之间的流动”。用例最基本的形式是代表用户操作,例如按城市检索天气数据、按坐标检索天气信息等等。为了符合单一职责 SOLID 原则,每个用例必须相互独立。

领域层应该完全独立于所有其他层。但是,当任何用例从与数据层共享的存储库中获取数据时,它如何保持独立呢?现在就来谈谈依赖倒置的艺术,这是 SOLID 原则之一。实际上,在领域层,我们创建一个抽象的存储库类来定义存储库必须执行的操作的契约,然后在数据层编写它的实际实现。

Repository: Repository 类作为单一事实来源,将检索数据并将其映射到实体模型的逻辑分离出来。实际上,该类从各种来源(REST API、本地数据库、缓存等)收集数据,并将其提供给应用程序的其他部分。其他组件不知道数据来自哪里;它们只是简单地使用它。

应用于天气应用程序

图片描述
在我们的例子中,领域层将分为三个不同的层:实体、存储库合同和用例。

- 数据

图片描述

此层负责从多个来源检索数据。它包含一个存储库类,该类实现领域契约,并确定是否返回新数据或缓存数据以及何时缓存这些数据。此外,数据源类负责从特定来源(通常是远程 API 或本地数据库)获取数据。

应用于天气应用程序

图片描述

我们的数据层由三层组成:数据源、存储库的实现和模型。由于每个数据源都提供数据,因此我们使用的是“models”文件夹而不是“entity”。实际上,每个模型都负责使用某些方法(fromJSON、toJSON 等)将非结构化数据(JSON 等)转换为 Dart 对象。

图片描述

下一步

在探索了天气应用程序的结构之后,我们将开始实现领域,这是最独立的层:下一步

文章来源:https://dev.to/marwamejri/flutter-clean-architecture-1-an-overview-project-struct-4bhf
PREV
在 JavaScript 中使用 `then()` 和 Async/Await
NEXT
将您的 Web 开发技能提升一个档次输入 viewport-fit 元标记和 CSS 环境变量。