整洁架构
ASP.NET Core整洁架构的起点。整洁架构只是一系列相同的松耦合、依赖倒置架构的最新命名。你也会看到它被称为六边形、端口和适配器或洋葱架构。
这种架构在Steve Smith和Julie Lerman的DDD基础课程中使用。
:school: 联系Steve的公司NimblePros,为您的团队提供整洁架构或DDD培训和/或实施协助。
解决Chrome错误
默认情况下,网站使用HTTPS,并期望您有一个用于localhost的自签名开发者证书。如果Chrome出现错误,请参阅此回答获取缓解说明。
目录
给个星星! :star:
如果你喜欢或正在使用这个项目来学习或启动你的解决方案,请给它一个星星。谢谢!
或者如果你觉得真的很慷慨,我们现在支持GitHub赞助 - 请看上面的按钮。
版本
主分支现在使用.NET 8。如果你需要以前的版本,请使用以下标记的提交:
了解更多
开始使用
要使用此模板,有几个选择:
- 使用
dotnet new
安装(推荐) - 下载此存储库(并根据需要修改)
使用dotnet CLI模板
首先,从NuGet (https://www.nuget.org/packages/Ardalis.CleanArchitecture.Template/)安装模板:
dotnet new install Ardalis.CleanArchitecture.Template
安装成功后,你应该能在dotnet new list
的模板列表中看到这个模板。寻找"ASP.NET Clean Architecture Solution",简称为"clean-arch"。
导航到你希望创建解决方案文件夹的父目录。
运行此命令在名为Your.ProjectName
的子文件夹中创建解决方案结构:
dotnet new clean-arch -o Your.ProjectName
Your.ProjectName
目录和解决方案文件将被创建,其中包含所有新的解决方案内容,正确命名并准备运行/测试!
示例:
感谢@dahlsailrunner帮助使这个功能正常工作!
已知问题:
- 名称中不要包含连字符。参见#201。
- 不要使用'Ardalis'作为你的命名空间(与依赖项冲突)。
关于控制器和Razor页面?
从版本9开始,此解决方案模板仅包括对使用FastEndpoints库的API端点的支持。如果你想使用我的ApiEndpoints库、Razor页面和/或控制器,你可以使用包含它们的最后一个模板,版本7.1。或者,在安装后可以轻松地将它们添加到此模板中。
添加Ardalis.ApiEndpoints
要使用Ardalis.ApiEndpoints而不是(或除了)FastEndpoints,只需添加引用并使用文档中的基类。
dotnet add package Ardalis.ApiEndpoints
添加控制器
你需要在Program.cs文件中添加对控制器的支持。你需要:
builder.Services.AddControllers(); // 如果你需要视图,则使用ControllersWithView
// 和
app.MapControllers();
一旦这些就位,你应该能够创建一个Controllers文件夹和(可选)一个Views文件夹,一切都应该按预期工作。就我个人而言,我发现Razor页面比控制器和视图要好得多,所以如果你还没有充分调查Razor页面,你可能想在选择视图之前现在就这么做。
添加Razor页面
你需要在Program.cs文件中添加对Razor页面的支持。你需要:
builder.Services.AddRazorPages();
// 和
app.MapRazorPages();
然后你只需在项目的根目录添加一个Pages文件夹,然后从那里开始。
使用GitHub存储库
要基于此存储库开始使用,你需要在本地获取一个副本。你有三个选择:fork、clone或下载。大多数情况下,你可能只想下载。
如果你只是想玩玩这个项目,或者你希望将它作为应用程序的起点,你应该下载存储库,解除zip文件的阻止,并将其解压到一个新文件夹中。
只有在你计划提交拉取请求时,你才应该fork这个存储库。或者如果你想在自己的GitHub账户中保留存储库快照的副本。
如果你是贡献者之一,并且你有提交访问权限,你应该克隆这个存储库。否则,你可能想选择其他选项之一。
运行迁移
你不应该需要这样做来使用这个模板,但如果你想在Infrastructure项目中正确设置迁移,你需要在运行迁移命令时指定该项目名称。
在Visual Studio中,打开Package Manager Console,运行Add-Migration InitialMigrationName -StartupProject Your.ProjectName.Web -Context AppDbContext -Project Your.ProjectName.Infrastructure
。
在使用CLI的终端中,命令类似。从Web项目目录运行这个:
dotnet ef migrations add MIGRATIONNAME -c AppDbContext -p ../Your.ProjectName.Infrastructure/Your.ProjectName.Infrastructure.csproj -s Your.ProjectName.Web.csproj -o Data/Migrations
要使用SqlServer,在Your.ProjectName.Infrastructure.StartupSetup
文件中将options.UseSqlite(connectionString));
改为options.UseSqlServer(connectionString));
。还要记得在Your.ProjectName.Web.Program
文件中将SqliteConnection
替换为DefaultConnection
,它指向你的数据库服务器。
要更新数据库,从Web项目文件夹使用此命令(将Clean.Architecture
替换为你的项目名称):
dotnet ef database update -c AppDbContext -p ../Clean.Architecture.Infrastructure/Clean.Architecture.Infrastructure.csproj -s Clean.Architecture.Web.csproj
目标
这个存储库的目标是提供一个基本的解决方案结构,可以用来构建基于领域驱动设计(DDD)的或简单的良好分层的、遵循SOLID原则的使用.NET Core的应用程序。在这里了解更多关于这些主题的信息:
- C#开发人员的SOLID原则
- 面向对象设计的SOLID原则(原始的、更长的课程)
- 领域驱动设计基础
如果你习惯于构建单项目应用程序或遵循传统的UI -> 业务层 -> 数据访问层"N层"架构的一组项目,我建议你在DDD基础之前先看看这两个课程:
Steve Smith还维护着Microsoft的参考应用程序eShopOnWeb及其相关的免费电子书。在这里查看它们:
请注意,这个项目和存储库的目标不是提供一个示例或参考应用程序。它只是一个模板,但有足够的部分就位,以显示你在设置实际解决方案时应该把东西放在哪里。不是无用的"Class1.cs",而是有几个真实的类在适当的位置。一旦你理解了它们为什么在那里以及你应该把自己的类似文件放在哪里,就删除它们。
历史和自荐部分
我已经使用这个入门套件教授ASP.NET Core的基础知识,使用领域驱动设计概念和模式有一段时间了(从ASP.NET Core还在预发布时就开始了)。通常,我在像DevIntersection这 在API端点和用例层级都进行验证是重复的,因此在此模板中选择在应用程序边缘(API端点)进行验证。这意味着用例项目的未来使用者也需要负责自己的验证,但在绝大多数情况下,API端点之外不会有其他用例消费者。
核心项目
核心项目是清洁架构设计的中心,所有其他项目依赖都应指向它。因此,它几乎没有外部依赖。核心项目应包括领域模型,包括:
- 实体
- 聚合
- 值对象
- 领域事件
- 领域事件处理程序
- 领域服务
- 规约
- 接口
- DTO(有时)
用例项目
这是一个可选项目,我包含它是因为很多人要求,而且删除比以后添加更容易。这也常被称为应用或应用服务层。用例项目按照CQRS组织为命令和查询。命令会改变领域模型,因此应始终使用仓储抽象进行数据访问。查询是只读的,因此不需要使用仓储模式,而可以使用最方便的查询服务或方法。然而,由于用例项目设置为依赖于核心而非直接依赖于基础设施,仍需要为其数据访问定义抽象。它可以使用规约等,有时可以帮助封装查询逻辑和结果类型映射。但它不必须使用仓储/规约 - 如果这是获取数据最有效的方式,它可以直接发出SQL查询或调用存储过程。
虽然这是一个可选的项目(没有它,API端点将直接与领域模型或查询服务一起工作),但它确实提供了一个不受UI影响的地方来添加自动化测试。
基础设施项目
应用程序对外部资源的大多数依赖应在基础设施项目中定义的类中实现。这些类应实现核心中定义的接口。如果你有一个具有许多依赖项的大型项目,可能有意义拥有多个基础设施项目(例如Infrastructure.Data),但对于大多数项目,一个带文件夹的基础设施项目就足够了。示例包括数据访问和领域事件实现,但你还会将电子邮件提供商、文件访问、Web API客户端等添加到此项目中,这样它们就不会增加核心或UI项目的耦合。
基础设施项目依赖于Microsoft.EntityFrameworkCore.SqlServer
和Autofac
。前者之所以使用是因为它内置于默认的ASP.NET Core模板中,是数据访问的最小公分母。如果需要,可以轻松替换为像Dapper这样的轻量级ORM。Autofac(以前的StructureMap)用于允许依赖关系的连接发生在最接近实现所在的地方。在这种情况下,可以在基础设施类中使用InfrastructureRegistry类来允许在那里连接依赖关系,而应用程序的入口点甚至不需要引用该项目或其类型。了解更多关于这种技术的信息。当前实现不包括此行为 - 这通常是我在研讨会中介绍并让学生自己添加的内容。
Web项目
应用程序的入口点是ASP.NET Core web项目。这实际上是一个控制台应用程序,在Program.cs
中有一个public static void Main
方法。它目前使用默认的MVC组织(Controllers和Views文件夹)以及大部分默认的ASP.NET Core项目模板代码。这包括其配置系统,使用默认的appsettings.json
文件加上环境变量,并在Startup.cs
中配置。该项目委托给Infrastructure
项目使用Autofac连接其服务。
SharedKernel项目
许多解决方案还会引用一个单独的共享内核项目/包。如果你需要在多个限界上下文之间共享代码(参见DDD基础),我建议创建一个单独的SharedKernel项目和解决方案。我进一步建议将其发布为NuGet包(很可能是在你的组织内部私有发布),并由需要它的项目作为NuGet依赖项引用。
之前此项目中包含了一个SharedKernel项目。但是,出于上述原因,我已将其作为一个单独的包,Ardalis.SharedKernel,当你使用此模板时应该用你自己的替换。
如果你想看另一个SharedKernel包的例子,我在更新的Pluralsight DDD课程中使用的那个在NuGet上可以找到。
测试项目
测试项目可以根据测试类型(单元、功能、集成、性能等)或它们测试的项目(Core、Infrastructure、Web)来组织,或两者兼而有之。对于这个简单的入门套件,测试项目是根据测试类型组织的,此解决方案中存在单元、功能和集成测试项目。在依赖关系方面,有三个值得注意:
-
xunit 我使用xunit是因为ASP.NET Core内部用它来测试产品。它工作得很好,随着ASP.NET Core新版本的发布,我相信它将继续与之良好配合。
-
NSubstitute 我使用NSubstitute作为基于行为的白盒测试的模拟框架。如果我有一个方法,在某些情况下,应该执行一个从对象的可观察状态看不出来的操作,模拟提供了一种测试方法。我也可以使用自己的Fake实现,但那需要更多的键入和文件。一旦你掌握了NSubstitute,它很棒,假设你不必模拟整个世界(在这种情况下我们不需要,因为有良好的模块化设计)。
-
Microsoft.AspNetCore.TestHost 我使用TestHost来测试我的web项目,使用其完整堆栈,而不仅仅是单元测试动作方法。使用TestHost,你可以发出实际的HttpClient请求而无需通过网络(因此没有防火墙或端口配置问题)。测试在内存中运行,非常快,请求会执行完整的MVC堆栈,包括路由、模型绑定、模型验证、过滤器等。
使用的模式
此解决方案模板内置了支持一些常见模式的代码,特别是领域驱动设计模式。以下是其中几个如何工作的简要概述。
领域事件
领域事件是将操作的触发器与其实现分离的绝佳模式。这在领域实体内部特别有用,因为事件的处理程序可以有依赖关系,而实体本身通常没有。在示例中,你可以在ToDoItem.MarkComplete()
方法中看到这一点。以下序列图演示了当通过Web API端点将项目标记为完成时,如何使用事件及其处理程序。