Domain层严禁引用Microsoft.EntityFrameworkCore,必须仅依赖.NET标准库;所有EF Core相关代码须置于Infrastructure层,领域实体不得含数据注解或EF接口,仓储接口禁止返回IQueryable,业务规则须封装在领域对象内。

Domain 层不能引用 Microsoft.EntityFrameworkCore,这是 DDD 在 C# 项目里落地的第一道硬门槛。跨过它,才能谈聚合、仓储、应用服务;踩不过去,就只是“带点业务名词的三层架构”。
为什么 DbContext 绝对不能出现在 Domain 项目中
领域项目(MyApp.Domain)必须只依赖 net6.0 或 netstandard2.1,不引入任何基础设施库。一旦你给 Order 类加了 [Table("Orders")] 特性,或让 Entity 实现 IEntityTypeConfiguration<order></order>,领域层就和 EF Core 绑死了。
后果很直接:
- 换数据库(比如从 SQL Server 切到 Cosmos DB)时,得重写所有实体类
- 想加事件溯源,发现
AggregateRoot已经被DbSet<t></t>的生命周期管理逻辑污染 - 单元测试里 mock 不了
Order,因为构造函数偷偷 new 了DbContext
正确做法是:把 DbContext 和所有 DbSet<t></t> 定义全放在 Infrastructure 层,Domain 层只暴露 IRepository<order></order> 接口,且该接口方法参数/返回值只能是 Order、IEnumerable<order></order>、Option<order></order> 这类纯领域类型。
IOrderRepository 接口里别写 IQueryable<order></order> 返回值
常见错误是这样定义:
public interface IOrderRepository
{
IQueryable<order> GetAll(); // ❌ 泄漏 IQueryable,等于把 EF 暴露给上层
Order GetById(Guid id);
}</order>
IQueryable<order></order> 是 EF Core 的查询构建器,不是领域概念。它允许调用方拼接 .Where()、.OrderBy(),导致业务规则分散在应用服务甚至控制器里。
替代方案有两条路:
- 用
Specification<order></order>封装查询意图,例如repo.Find(new ConfirmedOrdersInLastWeekSpec()) - 拆细接口,按用例建查询服务:
IOrderQueryService.GetConfirmedByCustomer(string customerId),返回IReadOnlyList<order></order>
关键判断标准:如果某个查询方法名里带 By + 字段名(如 FindByStatus),而且返回 IQueryable,那它已经越界了。
应用服务里禁止出现 if (order.Total > 1000) 这类判断
应用服务(ApplicationService)不是业务逻辑容器,它只做三件事:取数据 → 调领域对象 → 存结果。所有校验、状态转换、规则执行,必须收在 Order 自身的方法里。
比如取消订单,应该这样写:
public class OrderService
{
private readonly IOrderRepository _repo;
public OrderService(IOrderRepository repo) => _repo = repo;
<pre class="brush:php;toolbar:false;">public async Task CancelOrderAsync(Guid orderId)
{
var order = await _repo.GetByIdAsync(orderId);
order.Cancel(); // ✅ 业务规则在 Order 类内部
await _repo.UpdateAsync(order);
}}
而不是:
if (order.Status != "Pending") throw new InvalidOperationException(); if (order.Items.Count == 0) throw new InvalidOperationException(); // ❌ 规则散落、难复用、难测试
容易忽略的一点:领域对象的构造函数也得承担校验职责。比如 new Order(customerId, items) 应立刻检查 items 非空、总价合法,而不是等 Confirm() 才报错。
MediatR 处理领域事件时,别在 Handle 方法里调 _context.SaveChanges()
领域事件(如 OrderConfirmedEvent)应在事务提交后触发,否则会出现“事件发了但数据库回滚”的不一致。EF Core 默认在 SaveChanges 后才触发 DomainEvents,但如果你用 MediatR 手动发布,很容易在仓储实现里就调 mediator.Publish(new OrderConfirmedEvent(...)) —— 此时事务还没提交。
安全做法是:
- 在
UnitOfWork提交成功后,统一遍历并发布所有已收集的领域事件 - 或者用 EF Core 7+ 的
DbContext.SaveChangesAsync(cancellationToken)回调机制,在OnTransactionCommitted里发事件
最常被跳过的细节:领域事件处理器(IRequestHandler<orderconfirmedevent></orderconfirmedevent>)本身不能操作同一个 DbContext 实例,否则会引发并发访问异常或事务嵌套问题。它应该用独立的 DbContext 实例,或改用 Dapper 做轻量更新。











