{"id":24792699,"url":"https://github.com/swiftdo/flutter-tdd-clean-architecture","last_synced_at":"2026-02-06T23:02:06.138Z","repository":{"id":269803185,"uuid":"908419495","full_name":"swiftdo/flutter-tdd-clean-architecture","owner":"swiftdo","description":"flutter tdd 整洁架构 + 模块化","archived":false,"fork":false,"pushed_at":"2024-12-26T09:24:23.000Z","size":374,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-23T09:05:46.187Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swiftdo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-26T03:15:03.000Z","updated_at":"2025-02-21T10:48:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"e4274c27-318a-43ef-8158-81b069013ec8","html_url":"https://github.com/swiftdo/flutter-tdd-clean-architecture","commit_stats":null,"previous_names":["swiftdo/flutter-tdd-clean-architecture"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/swiftdo/flutter-tdd-clean-architecture","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftdo%2Fflutter-tdd-clean-architecture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftdo%2Fflutter-tdd-clean-architecture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftdo%2Fflutter-tdd-clean-architecture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftdo%2Fflutter-tdd-clean-architecture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swiftdo","download_url":"https://codeload.github.com/swiftdo/flutter-tdd-clean-architecture/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swiftdo%2Fflutter-tdd-clean-architecture/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29179565,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T22:12:24.066Z","status":"ssl_error","status_checked_at":"2026-02-06T22:12:09.859Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-29T20:40:06.023Z","updated_at":"2026-02-06T23:02:06.109Z","avatar_url":"https://github.com/swiftdo.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# my_flutter_app\n\n\n![](https://developer.qcloudimg.com/http-save/yehe-170434/3a5066a6fc24ff22f4f5fa0f3a4d6065.jpg)\n\n\nCore 层：提供全局的基础设施服务（如 NavigationService、Logger、ErrorHandler 等），其他层可以依赖于 Core 层，但 Core 层不依赖任何其他层。\n\nDomain 层：处理核心业务逻辑，如数据处理、验证、计算等。\n\nData 层：处理数据访问、API 请求、数据库操作等。\n\nPresentation 层：处理与用户界面的交互，如视图更新、页面跳转、UI 逻辑等。路由控制通常在这一层进行，负责根据业务逻辑（可能由 Domain 层 提供）来触发页面跳转。\n\n![](https://i0.wp.com/resocoder.com/wp-content/uploads/2019/08/Clean-Architecture-Flutter-Diagram.png?w=556\u0026ssl=1)\n\n## TODO:\n\n- [ ] 路由管理\n- [ ] 主题管理\n- [ ] 图片资源管理\n- [ ] 国际化\n\n## 各层职责\n在整洁架构（Clean Architecture）中，**领域层**、**数据层**和**展示层**各自承担着不同的职责，确保系统高内聚、低耦合并易于维护。以下是每一层的职责和作用：\n\n### **1. 领域层（Domain Layer）**\n\n#### **职责：**\n- **核心业务逻辑**：领域层是应用程序的心脏，负责实现应用的核心业务规则和领域模型。所有的复杂计算、业务流程、决策逻辑都应该在领域层进行处理。\n- **实体（Entities）**：定义应用程序中的核心数据对象和行为。例如，在电商应用中，`Product`、`Order` 可能是领域实体，包含与业务相关的属性和方法。\n- **用例（Use Cases）**：领域层定义了应用程序的用例，它们封装了应用的业务操作。例如，`CreateOrderUseCase`、`CalculateDiscountUseCase`，这些用例将由展示层调用，并与数据层交互。\n- **接口**：定义对外部服务（如数据源、API 等）的抽象接口，以保持与外部依赖的解耦。例如，`UserRepository` 接口可以定义数据层应提供的访问方法。\n\n#### **特点：**\n- **独立性**：领域层不依赖任何外部库、框架，甚至数据存储方式。它只包含业务逻辑。\n- **不与展示层和数据层耦合**：领域层仅通过接口与其他层交互，不关心如何获取或展示数据。\n\n#### **示例：**\n```dart\n// 领域实体\nclass Order {\n  final int id;\n  final List\u003cProduct\u003e products;\n  final double totalAmount;\n\n  Order(this.id, this.products, this.totalAmount);\n}\n\n// 用例\nclass CalculateTotalAmountUseCase {\n  double execute(List\u003cProduct\u003e products) {\n    return products.fold(0, (sum, product) =\u003e sum + product.price);\n  }\n}\n```\n\n---\n\n### **2. 数据层（Data Layer）**\n\n#### **职责：**\n- **数据获取与持久化**：数据层负责从外部数据源（如 API、数据库、本地存储等）获取和保存数据。\n- **数据转换**：数据层不仅负责获取数据，还需要负责将原始数据转换为领域层可使用的格式。比如，API 返回的数据可能是 JSON 格式，而领域层期望的是 `Order` 实体对象。\n- **实现存储接口**：数据层实现领域层定义的存储接口。例如，实现一个 `UserRepository` 接口，通过网络请求或数据库查询来获取用户数据。\n- **缓存处理**：在数据层中可以实现缓存策略，以减少不必要的网络请求或数据库查询，提高应用性能。\n\n#### **特点：**\n- **具体实现**：数据层直接与外部依赖（如数据库、网络、文件系统）进行交互，具体实现细节对领域层透明。\n- **解耦**：领域层定义的数据获取和存储接口，由数据层具体实现。这种方式保持了系统的灵活性和可扩展性。\n\n#### **示例：**\n```dart\n// 数据层接口\nabstract class UserRepository {\n  Future\u003cUser\u003e getUser(int id);\n  Future\u003cvoid\u003e saveUser(User user);\n}\n\n// 数据层实现\nclass UserRepositoryImpl implements UserRepository {\n  final ApiClient apiClient;\n\n  UserRepositoryImpl(this.apiClient);\n\n  @override\n  Future\u003cUser\u003e getUser(int id) async {\n    final response = await apiClient.get('/users/$id');\n    return User.fromJson(response.data);\n  }\n\n  @override\n  Future\u003cvoid\u003e saveUser(User user) async {\n    await apiClient.post('/users', data: user.toJson());\n  }\n}\n```\n\n---\n\n### **3. 展示层（Presentation Layer）**\n\n#### **职责：**\n- **UI 展示与交互**：展示层负责渲染应用的用户界面，并处理用户与应用的交互。展示层通过调用领域层的用例来获取数据和执行操作。\n- **UI 逻辑与状态管理**：展示层管理 UI 状态的变化，如用户输入、数据加载、错误显示等。它可能会使用状态管理方案（如 `Provider`、`Bloc`、`Riverpod` 等）来响应业务逻辑的变化。\n- **用户操作的转发**：展示层接收用户的输入（如按钮点击、表单提交等），并将操作转发给领域层对应的用例处理。\n- **UI 组件化**：展示层通常包括不同的 UI 组件（如按钮、列表、对话框等）以及布局管理。每个组件负责其独立的 UI 展示。\n\n#### **特点：**\n- **解耦业务逻辑**：展示层不包含业务逻辑，它只关心如何展示数据。所有复杂的业务计算和操作都应通过领域层的用例来完成。\n- **状态驱动**：展示层的状态应由领域层返回的数据或者用例执行的结果来驱动。\n- **对外暴露 UI 组件**：展示层通常包含诸如视图模型（ViewModel）或者状态管理的工具，用于管理和协调数据流动。\n\n#### **示例：**\n```dart\n// 展示层：视图模型\nclass UserViewModel with ChangeNotifier {\n  final GetUserUseCase _getUserUseCase;\n\n  User? _user;\n  User? get user =\u003e _user;\n\n  UserViewModel(this._getUserUseCase);\n\n  Future\u003cvoid\u003e loadUser(int id) async {\n    try {\n      _user = await _getUserUseCase.execute(id);\n      notifyListeners();\n    } catch (e) {\n      // 处理错误\n    }\n  }\n}\n\n// 展示层：UI 组件\nclass UserProfilePage extends StatelessWidget {\n  final UserViewModel viewModel;\n\n  UserProfilePage(this.viewModel);\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider\u003cUserViewModel\u003e(\n      create: (_) =\u003e viewModel,\n      child: Consumer\u003cUserViewModel\u003e(\n        builder: (context, viewModel, child) {\n          if (viewModel.user == null) {\n            return CircularProgressIndicator();\n          }\n          return Text('Hello, ${viewModel.user!.name}');\n        },\n      ),\n    );\n  }\n}\n```\n\n---\n\n### **总结：**\n\n| **层级**      | **职责**                                               | **与其他层的关系**                                   |\n|---------------|--------------------------------------------------------|-----------------------------------------------------|\n| **领域层**    | - 负责核心业务逻辑\u003cbr\u003e- 定义领域模型与用例\u003cbr\u003e- 提供接口 | - 不依赖其他层，使用接口与数据层交互，展示层调用用例 |\n| **数据层**    | - 数据获取、持久化\u003cbr\u003e- 数据转换与存储接口的实现       | - 实现领域层定义的接口，依赖领域层的接口           |\n| **展示层**    | - UI 展示与交互\u003cbr\u003e- 状态管理与用户输入转发            | - 调用领域层的用例执行操作，展示结果               |\n\n通过整洁架构，领域层、数据层和展示层之间的职责是分离的，它们通过接口进行交互，使得系统易于扩展、测试和维护。展示层主要负责界面和用户交互，领域层处理业务逻辑，数据层则负责数据的持久化和网络通信。\n\n## 参考\n\n- https://resocoder.com/tag/flutter/ 这个Flutter TDD Clean Architecture 系列的文章可以看下，\n受益匪浅，思想性的东西通用性很强。\n- 整洁架构在前端的设计思想与应用实践：https://cloud.tencent.com/developer/article/2324905\n\n![](https://developer.qcloudimg.com/http-save/yehe-170434/b7968ef5cb008703c5f42b0299c5974e.jpg)\n\n## feature\n\n应用程序的每个“功能”，例如获取有关数字的一些有趣的琐事，都将分为 3 层 -表示层、领域层和数据层。我们正在构建的应用程序将只有一项功能。\n\n\n## Domain 领域层\n\n域是内层，它不应该受到更改数据源或将我们的应用程序移植到 Angular Dart 的突发奇想的影响。\n它将仅包含核心业务逻辑（用例）和业务对象（实体）。\n它应该完全独立于其他所有层。\n\n这只是一种奇特的说法，我们创建了一个抽象的Repository类，定义了 Repository 必须执行的操作的契约 - 这进入了领域层。\n然后，我们依赖于域中定义的存储库“契约”，知道数据层中存储库的实际实现将履行该契约。\n\n其中的 repositories 是协议\n\nDomain Layer（领域层）通常由 Use Case 和领域模型组成，负责处理应用程序的业务逻辑和核心功能。业务逻辑与UI逻辑不同，UI逻辑定义如何在屏幕上显示内容，而业务逻辑定义如何处理事件和数据更改。\n\nDomain Layer（领域层）不会负责数据的显示，因为这是表现层的工作，也不负责获取和存储数据，这是数据层的工作。\n\n\nentities 实体\n跟业务逻辑相关的类\n\n## Data 层（数据层）\n\nrepositories 是实现\n\n我们处于外部世界和我们的应用程序之间的边界，因此我们希望保持简单。\n不会有Either\u003cFailure, NumberTrivia\u003e，\n而是，我们将只返回一个简单的NumberTriviaModel （从 JSON 转换）。\n错误将通过抛出异常来处理。\n处理这些“哑”数据并将其转换为Either类型将是Repository的责任。\n\nData Source 数据源\n数据源负责提供应用所需的数据，它们可能是远程API或者本地数据库。\n\nmodel 模型\n以 xxxModel 的形式命名的类，用于表示数据的结构和格式。\n\n\n\n## Presentation 表示层\n\npages 页面\nwidgets 小部件\nbloc: 处理与 domain 层和展示的粘合\n\n\n## 整洁架构的访问规则\n依赖方向：\n依赖必须是内向的（从外层指向内层），外层可以依赖内层，但内层绝对不能依赖外层。\n各层通过**接口（Interface）或抽象（Abstract）**进行通信。\n\n职责分离：\n每一层只关心自己范围内的职责，不能跨层越权。\n\n通信规则：\n内层不能直接访问外层，外层通过调用内层的接口完成通信。\n数据只能以**数据传输对象（DTO）或值对象（Value Object）**的形式在层间传递。\n\n\n## 各层职责和通信规则\n\n领域层（Domain Layer）\n职责：\n\n负责核心业务逻辑。\n包括领域模型（实体和值对象）、领域服务、业务规则。\n通信规则：\n\n不依赖其他层。\n提供明确的接口（如领域usecase）供应用层调用。\n通过 Repository 接口从数据层获取数据（不直接依赖具体实现）。\n\n\n## 对第三方组件的二次封装，这种属于哪一层\n\n第三方组件的二次封装通常属于 表示层（Presentation Layer） 或 Core 层，具体取决于该封装的目的和功能\n\n1. 表示层（Presentation Layer）封装\n   如果你对第三方组件进行二次封装，目的是为了简化 UI 相关的操作或者让组件更好地与应用的界面逻辑集成，那么这类封装应当属于 表示层。表示层封装第三方组件时，通常是在解决 UI 展示、用户交互或主题等方面的需求。\n\n示例：\nUI 控件封装：如果第三方库提供了某种 UI 控件（比如图表、列表等），并且你封装这个控件以便更方便地与应用的界面交互，那么这种封装是表示层的一部分。\n主题与样式整合：如果封装目的是为了使第三方组件的样式与应用的整体主题一致（例如，封装 flutter_awesome_dialog 来适配你的应用样式），则它也属于表示层的责任。\n\n\n2. Core 层封装\n如果你对第三方组件进行二次封装，目的是为了提供更统一的接口、简化与其他层的交互，或者做一些基础设施层面的扩展，那么封装应该属于 Core 层。Core 层通常负责管理基础设施服务和通用功能，例如网络请求、数据存储等，而对第三方组件的封装也可能是为了在不同层之间提供一致的接口。\n\n示例：\n网络请求库封装：假设你使用了 Dio 来进行网络请求，并且对 Dio 进行二次封装来统一处理 API 调用、错误处理、请求头配置等，这种封装属于 Core 层的责任。\n数据库库封装：例如封装 sqflite 或 hive，将其处理方式与应用的存储逻辑解耦，这样的封装属于 Core 层。\n\n3. Domain 层封装\n尽管第三方组件的封装一般不会直接放在 Domain 层，但如果封装与业务逻辑密切相关，并且封装后的组件对业务逻辑产生直接影响，那么它也可以考虑放在 Domain 层。\n\n示例：\n业务规则封装：如果封装某个第三方库的目的在于简化业务逻辑的处理，例如封装某个计算库来简化数学运算，并直接与业务模型和规则交互，那么它可以放在 Domain 层。\n\n\n具体实践\n通常情况下，第三方库的封装位置依赖于其使用场景：\n\n表示层封装：如果封装目的是简化 UI 交互或与 UI 逻辑的结合，封装应当放在表示层。\nCore 层封装：如果封装目的是简化底层服务的调用和管理（如网络请求、数据库、持久化等），封装应放在 Core 层。\nDomain 层封装：如果封装是为了简化业务模型的处理，封装可以放在 Domain 层。\n\n\n\n## 主题管理（Theme Management）\n\n主题管理主要涉及到用户界面的外观设置，如颜色、字体、样式等，通常由 表示层 来处理。但如果涉及到持久化（如保存用户的主题选择），则可以将相关功能放在 Core 层。\n\n- 表示层（Presentation Layer）：负责主题的切换、主题的展示和用户的交互操作。\n\n例如，通过 UI 控件让用户选择不同的主题（如浅色模式和深色模式），并动态更新应用的外观。\n\n- Core 层：负责持久化和加载用户的主题设置，确保应用能够在启动时恢复用户的主题偏好。\n\n例如，使用 SharedPreferences 来保存用户的主题选择，或使用某种配置文件来管理主题配置。\n\n示例：\n表示层：通过 ThemeData 和 ThemeMode 在应用中动态应用不同的主题。\nCore 层：通过 ThemeService 来保存和加载用户的主题选择。\n\n\n## 路由管理（Routing Management）\n\n路由管理是处理页面跳转、导航等 UI 相关的功能。通常，路由管理应该放在 表示层，因为它与用户界面的跳转、页面呈现直接相关。但是，路由可能依赖于 Domain 层 来决定跳转的条件（例如，用户是否登录）。\n\n表示层（Presentation Layer）：主要管理路由和页面跳转的逻辑。通常使用 Navigator 或类似的机制来控制页面跳转。\nCore 层：可以提供一个全局的 NavigationService，封装路由的操作，提供统一的接口。表示层会调用 Core 层的路由服务来触发页面跳转。\n\n\n示例：\n表示层：管理路由跳转，基于用户交互触发页面跳转。\nCore 层：提供统一的路由服务接口，封装路由逻辑。\n\n## 图片资源管理（Image Resources Management）\n\n图片资源管理主要是管理静态资源（如图片、图标）和可能的动态资源（如网络图片）。通常，图片资源的管理属于 表示层，因为它直接涉及到用户界面显示的内容。但是，图片的下载、缓存等可能涉及到 Core 层。\n\n- 表示层（Presentation Layer）：负责加载和展示图片资源，可能包括本地图片和网络图片。UI 控件（如 Image）和其他 UI 组件都会依赖这些资源来显示内容。\n- Core 层：可以封装一些图片缓存、下载和处理的逻辑（例如使用 cached_network_image 来缓存网络图片），从而避免重复的网络请求和资源加载。\n\n示例：\n表示层：加载和展示静态图片资源或从网络加载图片。\nCore 层：提供图片缓存、下载等服务，避免重复加载资源。\n\n\n总结\n主题管理：主要属于表示层（Presentation Layer），但与 Core 层配合来持久化主题设置。\n路由管理：主要属于表示层，但可以通过 Core 层提供全局的路由服务接口来实现解耦。\n图片资源管理：大部分属于表示层，但 Core 层可以封装缓存、下载等功能来优化性能和管理资源。\n\n\n## 事件总线\n事件总线是一种基础设施工具，主要用于模块之间的解耦通信。根据其用途和特性，事件总线通常属于 Core 层，因为它作为全局基础设施为应用提供服务。\n\n基础设施\n事件总线作为应用的通信机制，与具体业务逻辑无关，它是所有模块都可以依赖的工具。\n\n高复用性\n事件总线需要被多个 Feature 使用，而 Core 层正是为了解决这种跨模块的基础功能。\n\n解耦特性\n将事件总线放在 Core 层，其他模块通过注入或依赖方式使用，保持架构的低耦合\n\n如何管控事件总线\n为了防止事件总线被滥用或造成维护困难，可以通过以下方法进行管理：\n\n1. 明确事件分类\n   - 全局事件\n    比如用户登录、网络状态变化等全局状态的通知。\n   - 局部事件\n   比如模块内的状态更新事件，仅限于模块间的通信。\n\n将全局事件和局部事件分开管理，避免事件的无限扩散。\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftdo%2Fflutter-tdd-clean-architecture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswiftdo%2Fflutter-tdd-clean-architecture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftdo%2Fflutter-tdd-clean-architecture/lists"}