一个使用 ObjectBox 持久化存储的 Flutter 实现的 4+2分层架构结构

这是在 4+2分层架构 及其配套出版物 4+2分层架构 – A Flutter Implementation 中描述的架构的实现。

此模板扩展了 flutter_layered_template,将“内存中”存储转换为使用 ObjectBox 库。

这实际上是从 NotifierAPI 到 StreamAPI 的转换。在这种情况下,外层(Domain)的变化会影响内层(Domain),因为新库提供了不同的 API,我们最好使用它!Stream 和 Future API 常用于处理远程调用和异步调用,但对于观察式内容通知,它也非常方便,可以替代 Notifier API。尽管我们不得不更改 Domain 的 Repository 和 UseCase 接口的 API,但影响得到了很好的定义,主要是在 UseCase 的 API 客户端(Presentation Layer)中。在这里,我们看到了 Riverpod consumer API 的强大之处……将 Presentation Widgets 中的 NotifierAPI 转换为 StreamAPI 是简单而优美的!

此模板

  • 提供了项目结构,其中包含所有建议的层,每层都位于一个单独的包中。
  • 在可运行的示例应用程序中实现了简单的域。
  • 有三个 Flutter 依赖项需要注意

架构概述

Project Structure

4+2分层架构 描述了 关注点分离 在软件开发中的重要性。当应用于项目架构时,我将应用程序组织为

  • 4个主要层
    • Domain Layer(业务规则)
    • Data Layer(持久化)
    • Service Layer(外部服务)
    • Presentation Layer(UI)
  • 2个附属层
    • Core Layer(共享库)
    • Dependency Injection Layer(依赖倒置)

层对象

此提案中的一个关键实现是为每个结构化层使用一个 AppLayer 对象,每个层的配置以及层之间的交互是这些对象的职责。初始化由 依赖注入 的层对象编排。

DILayer object 在 main 方法中初始化,在该调用中 DILayer object 获取并初始化所有其他层对象。然后,它使用从提供者层(DataLayerServiceLayer 对象)检索到的所有必需依赖项(接口实现)来配置 DomainLayer object

再次强调,使用这些层对象的驱动力是 关注点分离:没有任何层需要了解其他层的内部实现。


项目结构

Project Structure

所有代码都分离到各个层,每层组织为一个独立的内部包。这种关注点分离使得层间依赖关系明确,并且测试是模块化的。

主项目结构,如下图所示,包含

  • flutter 项目文件:pubspec.yamlbuild** 等
  • 包含 main.dart 的 lib 文件夹
  • 包含所有层的 packages 文件夹
    • _core_layer
    • _data_layer
    • _di_layer
    • _domain_layer
    • _service_layer
    • _presentation_layer
  • 主项目的 test 文件夹

每个层文件夹都有自己的包结构,包含

  • pubspec.yaml
  • 该层的 test 文件夹

主项目和外层 pubspec.yaml 使用路径依赖来引用内层,例如,在 _domain_layer 的 pubspec 中可以看到

dependencies:
  _core_layer:
    path: ../_core_layer

在此结构图中,我展示了我的 VSCode 资源管理器使用 MultiRoot 功能(又名 Workspace files),将每个层设置为一个根文件夹。我使用此 MultiRoot 功能并结合 Explorer Exclude 扩展 来聚焦查看我的项目。

图像仅显示了用于举例的 Domain Layer

除了图像中显示的这些文件外,还有许多用于 Flutter、VSCode、GIT 等的配置文件。由于我们处理 6 个额外的内部包,因此我们会遇到很多这些配置文件,并且在 VSCode 资源管理器中的代码导航会变得非常混乱。因此,建议使用此 Explorer Exclude 扩展。

以下是我针对 MuiltiRootExplorer Exclude 工作区的配置。此配置在此模板的 .vscode/layered_template.code-workspace 文件中提供。

{
  "folders": [
    { "path": ".." },
    { "path": "../packages/_core_layer", "name": "Core Layer" },
    { "path": "../packages/_data_layer", "name": "Data Layer" },
    { "path": "../packages/_di_layer", "name": "DI Layer" },
    { "path": "../packages/_domain_layer", "name": "Domain Layer" },
    { "path": "../packages/_service_layer", "name": "Service Layer" },
    { "path": "../packages/_presentation_layer", "name": "Presentation Layer" }
  ],
  "settings": {
    "files.exclude": {
      "**/.git": true,
      "**/.svn": true,
      "**/.hg": true,
      "**/CVS": true,
      "**/.DS_Store": true,
      "**/Thumbs.db": true,
      "**/.dart_tool": true,
      "**/.idea": true,
      "**/.vscode": true,
      "**/*.iml": true,
      "**/.metadata": true,
      "**/.packages": true,
      "**/CHANGELOG.md": true,
      "**/LICENSE": true,
      "**/README.md": true,
      "**/analysis_options.yaml": true,
      "**/pubspec.lock": true,
      ".code-workspace": true,
      ".flutter-plugins": true,
      ".flutter-plugins-dependencies": true,
      ".gitignore": true,
      "android": true,
      "build": true,
      "packages": true
    },
    "explorerExclude.backup": null
  }
}

示例域

此模板定义了一个简单的域模型,具有“内存中”临时持久化。持久化是使用 StateNotifier 实现的,用于保存实体并通知存储状态更改。目的不是实现一个功能齐全的应用程序,而是提供一个具有完整分层架构结构的简单应用程序。


使用此模板进行新的 Flutter 项目

这是一个分层架构项目的启动模板。

  1. 在 github 的此模板主页上点击 使用此模板

    https://github.com/cc-nogueira/flutter_layered_template_objectbox/

  2. 将您的存储库克隆到您的开发机器

   git clone https://github.com/your-repo/your-project.git
  1. 更改 pubspec.yaml 中的名称
   name: your-project
  1. 运行提供的实用程序 project.dart,它会初始化主项目和所有层包,创建未存储在 git 中的配置文件(项目和构建文件)。

   cd <your-project>
   dart project.dart init
  1. 从文件打开工作区

   Rename workspace file from .vscode/layered_template.code-workspace to <your-project>.code-workspace
   Open VSCode
   Choose "Open Workspace from File...: from the File menu
   Select <your-project>/.vscode/<your-project>.code-workspace

提供的 project.dart 实用程序包含初始化、清理和构建项目及其内部包的命令。您始终可以使用终端在特定的包文件夹中运行命令,例如,您可以在 domain 文件夹中运行 build_runner 来生成 json 或 freezed 文件。但是,当您想为所有层运行 build_runner 或 clean 时,最好使用 dart project.dart 运行具有特定命令。


参考

出版物

视频

GitHub

查看 Github