Clean Architecture与DDD单库中复杂查询处理程序的实现位置疑问
Clean Architecture下复杂查询的处理方案
背景
我遵循Clean Architecture原则,将持久化实现细节隔离在Application Core之外:
- 仓储接口位于Application Core项目中
- Application Core项目不引用任何数据访问库(ORM)
- Infrastructure项目引用ORM并实现仓储
遇到的困惑
在处理「获取包含客户详情及总价的逾期未结订单」这类查询时陷入两难:
- 该查询包含业务逻辑(订单判定为未结的条件、逾期的判定规则等)
- 需要性能优化(直接计算订单项总价,而非获取所有订单项)
- 已将
OpenOrdersQueryDTO置于Application Core层,但不确定查询处理程序的放置位置:放在Infrastructure会把业务逻辑混入其中;放在Application Core则无法直接进行数据访问。
解决方案
核心思路是分离查询的业务规则与数据访问实现,具体步骤如下:
1. 在Application Core定义查询契约与业务规则
- 将查询的业务判定逻辑封装为独立的规则类,比如
OrderIsOpenSpecification、OrderIsOverdueSpecification,明确未结、逾期的业务标准,确保业务逻辑完全留在Application Core。 - 定义查询处理接口,比如
IQueryHandler<OpenOrdersQuery, IEnumerable<OpenOrdersResult>>,仅声明输入输出,不涉及任何数据访问细节。
2. 在Infrastructure实现查询处理程序
- 实现Application Core中定义的查询处理接口,这里可以直接使用ORM进行性能优化(比如用LINQ直接计算订单项总价、关联客户表)。
- 在处理程序中,调用Application Core的业务规则类过滤订单,保证业务逻辑不会混入数据访问代码中。
3. 通过依赖注入解耦
在应用启动时注册Infrastructure中的查询处理实现,让Application Core通过接口调用,无需关心具体的数据访问细节。
代码示例
Application Core 层
// 查询DTO public class OpenOrdersQuery { } // 查询结果DTO public class OpenOrdersResult { public Guid OrderId { get; set; } public string CustomerName { get; set; } public decimal TotalAmount { get; set; } public DateTime OrderDate { get; set; } } // 查询处理接口 public interface IQueryHandler<TQuery, TResult> { Task<IEnumerable<TResult>> Handle(TQuery query); } // 业务规则:未结订单判定 public class OrderIsOpenSpecification { public bool IsSatisfiedBy(Order order) { // 业务规则示例:订单状态为待支付且未取消 return order.Status == OrderStatus.Pending && !order.IsCancelled; } } // 业务规则:逾期订单判定 public class OrderIsOverdueSpecification { public bool IsSatisfiedBy(Order order) { // 业务规则示例:订单超过付款期限7天 return DateTime.Now > order.PaymentDueDate.AddDays(7); } }
Infrastructure 层
public class OpenOrdersQueryHandler : IQueryHandler<OpenOrdersQuery, IEnumerable<OpenOrdersResult>> { private readonly AppDbContext _dbContext; private readonly OrderIsOpenSpecification _openSpec; private readonly OrderIsOverdueSpecification _overdueSpec; public OpenOrdersQueryHandler(AppDbContext dbContext, OrderIsOpenSpecification openSpec, OrderIsOverdueSpecification overdueSpec) { _dbContext = dbContext; _openSpec = openSpec; _overdueSpec = overdueSpec; } public async Task<IEnumerable<OpenOrdersResult>> Handle(OpenOrdersQuery query) { // 利用ORM实现性能优化的查询,同时应用业务规则过滤 return await _dbContext.Orders .Where(o => _openSpec.IsSatisfiedBy(o) && _overdueSpec.IsSatisfiedBy(o)) .Join(_dbContext.Customers, o => o.CustomerId, c => c.Id, (o, c) => new { o.Id, c.Name, Total = o.OrderItems.Sum(oi => oi.Quantity * oi.UnitPrice), o.OrderDate }) .Select(result => new OpenOrdersResult { OrderId = result.Id, CustomerName = result.Name, TotalAmount = result.Total, OrderDate = result.OrderDate }) .ToListAsync(); } }
这种方式既保证了业务逻辑的内聚性(留在Application Core),又让Infrastructure专注于高效的数据访问实现,完全符合Clean Architecture的隔离原则。
内容的提问来源于stack exchange,提问作者Liero
相关产品推荐
相关产品推荐

