DDD要求领域模型类不包含数据库操作,业务逻辑应集中于Entity等类中,数据读写须经应用服务协调仓储,仓储接口仅返回领域对象,避免基础设施泄漏。

领域模型类里别直接写数据库操作
DDD 的核心是让业务逻辑集中在 Entity、ValueObject、AggregateRoot 这些类里,而不是混进 EF Core 的 DbContext 或仓储实现。一旦你在 Order 类里调用 _context.SaveChanges(),就等于把持久化细节泄漏进领域层,后续换数据库或加事件溯源会非常痛苦。
- 领域实体只暴露业务行为方法,比如
Order.Confirm()、Order.AddLineItem(),内部只做状态校验和业务规则执行 - 所有数据读写必须经由应用服务(Application Service)协调,再委托给仓储(
IRepository)或领域事件处理器 - EF Core 的
DbSet不能出现在领域项目中——领域项目应只引用 .NET Standard 库,不依赖任何 ORM
仓储接口定义要严格遵循“仅返回领域对象”原则
仓储不是数据访问层的包装,而是领域对“已存在聚合”的抽象。如果 IOrderRepository.FindByCustomerId() 返回的是 IQueryable 或 OrderDto,说明它已经越界了。
- 接口方法只能返回
Order、IEnumerable、Option这类纯领域类型,绝不能暴露DbSet、IQueryable、DataRow等基础设施类型 - 分页、排序、过滤等非领域需求,应通过 Specification 模式封装,例如
new OrderByStatusAndDateSpec(OrderStatus.Confirmed, DateTime.Today),再由仓储实现去翻译成 SQL - 避免在仓储接口里塞太多查询方法,宁可多建几个细粒度接口(如
IOrderQueryService),保持仓储专注“按 ID 加载/保存聚合”
应用服务里别做领域逻辑判断
应用服务(Application Service)本质是用例入口,职责是编排:取数据 → 调领域对象 → 存结果 → 发事件。如果里面出现 if (order.Total > 1000) { ... } 这类判断,说明领域规则被抽离了。
- 所有业务规则必须实现在领域对象内部,比如
Order.CanBeRefunded()或Order.ApplyDiscount(DiscountRule rule) - 应用服务只负责组装上下文:从仓储取
Order,调它的方法,捕获DomainException,再决定是否回滚或记录日志 - 不要在应用服务里 new 领域对象——用工厂(
IOrderFactory)或构造函数约束(如Order.Create(...))来确保不变性
ASP.NET Core 里怎么隔离领域层和 Web 层
常见错误是 Controller 直接接收 OrderDto 并 new 出 Order,或者把 DbContext 注入到领域服务里。这会让整个分层失效。
- Web 层(Controllers)只处理 HTTP 协议相关的事:绑定参数、验证 DTO、返回 StatusCode —— 它不应知道
AggregateRoot是什么 - DTO 和领域对象之间必须手动映射(不用 AutoMapper 自动反射映射),因为字段语义可能不同(比如 DTO 有
CustomerIdString,而领域里是强类型的CustomerId值对象) - 依赖注入配置时,领域项目不引用 Microsoft.EntityFrameworkCore;仓储实现(
EFOrderRepository)放在 Infrastructure 项目,并在那里注册DbContext
最常被跳过的一步是:没为每个聚合设计明确的生命周期边界。比如把 Order 和 Inventory 放进同一个仓储,或让 Order 直接调用 InventoryService.DecreaseStock()。这时候“聚合根保护一致性”的意义就没了。











