clean architecture核心是依赖单向性,domain必须零nuget依赖、纯.net类库;禁止在domain中引用ef core或system.text.json等外部包,否则架构“漏气”。

直接套用 GitHub 上的 Clean Architecture 模板,90% 的人会在 Domain 层悄悄引用 Microsoft.EntityFrameworkCore 或 System.Text.Json —— 这已经不是“整洁”,而是“漏气”。Clean Architecture 不是文件夹命名比赛,核心只有一条:依赖只能由外向内,Domain 必须零外部依赖、纯 .NET 类库。
Domain 项目为什么不能装 NuGet 包
很多人把 Domain.csproj 当成普通类库,顺手加了 FluentValidation 或 MediatR,结果编译时看似正常,运行时却在 Application 层注入失败。根本原因是:Domain 是整个系统的“契约中心”,它定义接口(如 IProductRepository),但不关心实现;一旦它依赖了 EF Core 的特性或序列化工具,就等于把数据库或 HTTP 细节提前锁死在业务核心里。
- Domain 项目 SDK 必须是
<targetframework>net6.0</targetframework>或更高,且 不能有任何<packagereference></packagereference> -
[Key]、[Required]、[JsonIgnore]这类特性一律禁止出现在Product.cs或Customer.cs中 - 值对象(如
Money、EmailAddress)必须用public readonly struct或私有构造 + 工厂方法,禁止 public set - 领域异常(如
InsufficientStockException)继承自Exception即可,不要引用Microsoft.Extensions.Logging
Application 层如何避免写成“胶水搬运工”
Application 层最常犯的错,是把 Mapper.Map()、JsonSerializer.Serialize()、HttpClient.PostAsync() 全塞进 CreateProductCommandHandler。这不是编排,是污染。
- 所有输入输出必须是 Application 自己定义的 DTO,比如
CreateProductCommand和CreateProductResponse,它们和 Domain 的Product实体完全无关 - 领域服务必须通过接口注入,且该接口声明在 Domain 层(如
IInventoryDomainService),实现放在 Infrastructure - 事务边界控制在这里,但具体实现(如
_context.SaveChangesAsync())必须封装在IUnitOfWork接口后,Application 层只调_unitOfWork.CommitAsync() - 禁止在 Handler 里 new 任何跨层对象(如 new HttpClient()、new DbContext())—— 所有依赖走 DI,类型必须是 Domain 或 Application 定义的抽象
Infrastructure 注入时 Program.cs 容易漏掉的三处绑定
ASP.NET Core 6+ 的 Program.cs 是整个架构的“粘合点”,少配一处,Domain 接口就永远找不到实现。
-
services.AddDbContext<appdbcontext>(...)</appdbcontext>必须在 Infrastructure 的扩展方法里注册,且AppDbContext要继承DbContext并只出现在 Infrastructure 项目中 -
IProductRepository接口定义在 Domain,但实现类ProductRepository在 Infrastructure,必须显式绑定:services.AddScoped<iproductrepository productrepository>()</iproductrepository> - 如果用了 MediatR,
IMediator的注册要放在 Application 层扩展方法里,但所有IRequestHandler实现类必须在 Application 项目中 —— Infrastructure 里不能出现任何HandleAsync方法
为什么 WebApi / Presentation 层不能引用 Domain 的具体类型
控制器里写 return Ok(product) 看似没问题,但如果 product 是 Domain 的 Product 实体,就等于把领域模型直接暴露给了 HTTP 层。一旦你后续要在 Product 上加 EF Core 的延迟加载代理或 JSON 循环引用逻辑,API 就会崩。
- Controller 的 Action 参数只能是 Application 的 Request DTO(如
CreateProductCommand),返回值只能是 Response DTO(如CreateProductResponse) - ViewModel、DTO、Entity 三者严格分离:Domain 有
Product,Application 有CreateProductCommand,WebApi 有ProductDto(用于 GET 返回) - AutoMapper 配置必须放在 Presentation 或 Application 层,且 Profile 中的映射规则不能引用 Infrastructure 或 Domain 的实现类(只允许接口和 DTO)
真正难的不是建四个文件夹,而是每次新增一个类时,都得问一句:它属于哪一层?它的依赖是否反向穿透了内层?哪怕只是给 Domain/Exceptions 加了一个 using System.Net.Http;,整套架构的信任链就断了。











