清晰的架构 – 整合仓库模式






4.98/5 (19投票s)
如何整合仓库模式。
目录
- 引言
- 目的
- 范围
- 必备组件
- 解决方案\环境设置
- 代码库
- SQL数据库脚本
- Visual Studio 扩展
- 组件与Nuget包
- 对整洁架构的简要说明
- 核心 (领域与应用)
- 基础结构
- UI应用
- Visual Studio - 解决方案结构
- Blazor 应用运行
- 添加现有数据库实体与DBContext
- 实体
- DBContext
- 连接字符串 (API项目)
- 整洁架构 – 我们解决方案的主要分层
- 领域 (类库)
- 应用 - 用例\业务逻辑 (类库)
- 基础设施 (类库)
- 应用层 – 接口
- DTOs (数据传输对象)
- Swagger 已启用 (启动设置 API)
- Polly 重试策略
- 测试 Polly
- 日志记录 (服务器端)
- 单元测试 – MSTest\Faker\Bogus
引言
本文档详细概述了如何将仓储模式 (Repository Pattern) 整合到整洁架构模式中。它涉及了您现有 MVC 模式中许多常见的方面(日志记录、单元测试、依赖注入、EF Core 等)。
目的
产品架构文档 (PAD) 提供了关于如何将数据库优先方法整合到整洁架构方法中的全面架构概述——涉及哪些组件以及它们如何相互关联。
范围
本 PAD 的范围是传达生产整洁架构模式和仓储模式所需的概念。
必备组件
- 理解 MVC 或 DDD 架构模式
- 理解数据库优先方法 (仓储模式)
- 了解 SQL Server – 运行 SQL 脚本
- 了解 .NET Core\6
- 已安装 SQL Server (包括 SSMS) 和 Visual Studio (免费+版本)
- MSTest 的基本知识
解决方案\环境设置
代码库
使用此链接克隆代码仓库 https://github.com/Bert0Neill/CleanArchitectureDemo.git。
SQL数据库脚本
Visual Studio 扩展
请从此处下载并安装 Visual Studio 的 **EF Core Power Tools** 扩展 -https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools
组件与Nuget包
为了使解决方案尽可能真实,我使用了您当前在现有架构模式中会使用的以下包和组件——它们已集成到解决方案项目中,在需要的地方使用。
- Faker\Bogus
- MOQ
- MSTest
- IHttpFactory
- Http Polly (API 重试)
- EF Core 6
- SQL Server 数据库
- EF Power Core Tools
- 中间件 (异常处理)
- 日志记录 (基于文件)
- GuardRails
- Blazor WASM
- Benchmark.Net (性能)
- AutoMapper
- Swagger UI
对整洁架构的简要说明
整洁架构模式的概念已经存在十多年,最初由 Robert Martin(更广为人知的名字是 Uncle Bob)构思。Uncle Bob 的关键词是 **可互换性 (Interchangeable)**。下图中的蓝色圆圈内的一切都是可互换的,例如,UI 可以从 Angular 换成 React,或者数据库可以从 Oracle 迁移到 MySQL,而底层分层无需更改。
将所有接口(基础设施和应用)放在一个项目中的概念,将更容易进行单元测试和模拟。
但整洁架构的主要基本原理是,MVC 的扩展性不足,也无法实现分层之间的松耦合。在整洁架构中,依赖关系仅面向内部(这满足了 SOLID 原则中的 DI)。在 MVC 中,Model View 同时充当 UI 和 Controller 层,这会变得非常庞大且难以测试(由于紧耦合)。MVC 已经服务于软件行业 20 多年,但行业希望在未来 20 年内采用一种新的、更精简的架构模式——一种可扩展、可互换、解耦的模式。
核心 (领域与应用)
- 应用层是核心之上的顶层或父层,它只依赖于领域层。其他层,如基础设施层或 UI 层,将依赖于应用层。
- 应用层包含“接口”、“业务逻辑”等。
- 领域项目主要包含“实体”、“自定义异常”、“枚举”等。
基础结构
基础设施层处理“数据库”、“外部 API 调用”、“缓存”等。基本上,基础设施层处理所有外部资源。基础设施层依赖于应用层内的“接口”。由于依赖倒置,我们的应用层将是松耦合的,这使得测试变得容易。
UI应用
UI 应用层消费“应用核心”来产生结果。在实时场景中,UI 应用层从不依赖于基础设施层,但我们需要在 UI 项目中引用基础设施层来注册服务依赖注入。因此,UI 项目除了依赖注入外,不应使用基础设施层的任何代码。
Visual Studio - 解决方案结构
解决方案被分割成 **解决方案文件夹**,每个文件夹内有一个项目(根据项目大小或复杂性,一个分层可能包含多个项目,因此将一个分层拆分成多个项目在维护方面可能更有意义)。
CleanArchitecture.Domain
- “应用核心” - 一个类库模板项目CleanArchitecture.Application
- “应用核心” - 一个类库模板项目CleanArchitecture.Infrastructure
- “基础设施” - 一个类库模板项目CleanArchitecture.Api
- “API” - 一个 Web API 模板项目CleanArchitecture.Blazor
- “Web UI” - 一个 Web UI 模板项目CleanArchitecture.UnitTests
- “单元测试” - 一个类库模板项目
Blazor 应用运行
下图展示了最终的 UI 屏幕,这是客户端 Web 应用程序对 API 控制器的调用,API 控制器反过来调用应用服务(用例\业务逻辑),应用服务调用相应的基础设施类来执行外部任务。
添加现有数据库实体与DBContext
实体
数据库实体将保存在领域项目本身中,右键单击领域项目,然后从上下文菜单中选择 **EF Core Power Tools**,然后选择 **Reverse Engineer**。
然后会提示您选择要反向工程数据库模型的数据库。
接下来,您可以选择相应的数据库表来生成实体。
确保从“**生成内容**”下拉列表中选择 **仅实体 (EntityTypes only)**,因为我们只想创建实体,而不是在 `Domain` 项目中创建 `DBContext`。输入创建实体的路径,命名空间应该正确,因为您右键单击的是 `Domain` 项目。
DBContext
右键单击基础设施项目,再次选择 **EF Core Power Tool**,以将 `DBContext` **反向工程**(到 `Application` 项目中)。
确保从下拉列表中选择 **仅 DbContext (DbContext Only)**,并输入要生成 **DBContext 类** 的路径。
然后,这将在相应的项目中生成实体和 `DBContext` 的以下结构。
连接字符串 (API项目)
连接字符串放在 **API 项目的 Programs 类** 中,因为我们正在使用 `Application` 项目,而 `Application` 项目反过来需要注入将使用 `DBContext` 的基础设施类。
在你的 _Program.cs_ 类中添加对 `DBContext` (来自基础设施项目的 `MusicContext`) 的引用,因为它只允许将 DI 执行到 `Application` 类中。
在 _Appsettings_ 文件中为连接字符串添加一项条目。
整洁架构 – 我们解决方案的主要分层
整洁架构方法非常灵活,可以适应您希望使用的任何设计模式(例如,工厂模式与装饰器模式),但在我们的示例中,我使用了常见的仓储模式方法。
领域 (类库)
领域项目将托管企业范围内的实体\模型、自定义异常、枚举等,但它没有任何依赖项、项目或类引用、业务逻辑等。
提示 – “Entity
”这个词来源于 SQL Server 的“Identity”属性——这意味着实体必须有一个主键。
应用 - 用例\业务逻辑 (类库)
将这些服务视为应用程序的业务逻辑\用例层,是 UI 到域的通道,并执行解决方案所需的必要应用程序逻辑。应用层将使用域模型,但使用基础设施层与外部世界通信,从而使用这些结果来执行其业务逻辑(对多个基础设施调用的结果进行切片和切块,然后将这些结果作为(例如)DTO 返回给客户端)。
注意:仅添加了领域作为引用项目。
基础设施 (类库)
这些类负责外部基础设施通信,如数据库存储、文件系统、外部系统/API/服务等。我们可以在此项目文件夹下为外部插件或 SDK 添加更多的类库。
本质上,基础设施层在技术上并非必需,因为您可以设计一个不与外部世界交互、并且所有业务逻辑都自行处理的应用程序,这当然是反常的!
它是系统的最外层,不应了解内层。
注意:已添加应用类作为引用。
注意:UI 应用层从不依赖于基础设施层,但我们需要在 UI 项目中引用基础设施层来注册服务依赖注入。因此,UI 项目除了依赖注入外,不应使用基础设施层的任何代码。
应用层需要注入基础设施类,所以这必须来自 Web API 层(UI 层不使用基础设施,仅用于 DI 到应用层)。
API 项目的 _Programs.cs_ - 配置基础设施 DI
消费已注入基础设施类的应用服务
下图展示了项目之间的内部引用结构。API 项目必须引用应用层,以便它可以调用各种业务逻辑服务(它仅为 DI 目的而引用基础设施)。
应用层 – 接口
整洁架构方法的一个主要特点是将所有接口都放在一个地方,即应用项目。这表明只有这一层在生成解决方案其他部分的单元测试时才需要模拟。所有模拟都在一个地方,使单元测试更容易。
因此,下面我正在为基础设施项目中的基础设施仓储类生成一个接口,然后在它被创建后将其移动到应用项目中。
注意:默认情况下,它将在 `Infrastructure` 类中创建接口,我们将在其创建后进行移动。
DTOs
尽管 DTO 本身也是实体,因此您可能认为它们应该放在 `Domain` 项目中,但任何项目中 DTO 的经验法则是,它们应该放在它们(最)被使用的项目中。对于 Web 相关解决方案,这应该是 API 层——因为来自客户端的数据将是 DTO,而发送到客户端的数据将是映射的实体(到 DTO)。
但是,将 DTOs 放在靠近它们使用的地方的另一个有力论据是,如果它们放在 `Domain` 项目中,即使新的 API 希望使用应用服务,它们也将具有相同的结构——因此,为了使 API\客户端更灵活,`Domain` 项目不应该规定客户端接收数据的格式,这应该留给每个单独的 API 项目。
Swagger 已启用 (启动设置 API)
在向 API 项目添加 Swagger 包后,您可以按如下方式配置您的项目。
修改你的 _Programs.cs_ 类以考虑你的 Swagger 设置。
Polly 重试策略
Polly 配置在 Blazor UI 应用程序的 _Program.cs_ 文件中,下面我使用 _appsettings.json_ 文件来读取各种设置(重试次数和基本 URL)并将它们分配给每个 API 调用——下面我指定将 API 调用重试最多 5 次,并且它会抖动重试,以免立即用调用压垮服务器。
测试 Polly
只运行 Blazor 应用程序(不与 API 同时运行),然后再次从左侧菜单中选择“获取相册数据”选项——请注意,它会重试 5 次(来自我们的 appsetting)然后放弃。
日志记录 (服务器端)
配置 NLog 以便您按照自己的意愿记录数据;下面我只是在 API 项目中设置它,以便将所有内容记录到日志文件(使用项目根目录上的 _nlog.file)。
在 _Programs.cs_ 中配置 NLog。
示例输出
单元测试 – MSTest\Faker\Bogus
下面,我正在使用 Bogus 包配置单元测试和模拟数据,其中我正在伪造从仓储调用返回的数据,因此我可以单元测试存储在应用项目中的业务逻辑\用例——您可以在解决方案项目中查看单元测试代码。