如何在领域层仓储接口中使用应用层DTO?(仓储模式)
问题描述
在学习ASP.NET整洁架构时,遇到以下问题:
应用层中有一个GetAllRestaurants查询,需要返回分页的餐厅对象。采用仓储模式,但领域层无法访问其他层,导致IRestaurantRepository中无法定义依赖应用层DTO或对象的方法。
应用层的Handle方法代码:
public async Task<PagedResult<List<RestaurantDto>>> Handle(GetRestaurantQuery request, CancellationToken cancellationToken) { var restaurants = await _restaurantRepository.GetAllRestaurants(request); // etc... }
领域层的IRestaurantRepository尝试使用应用层的GetRestaurantQuery,但因依赖问题无法运行:
using Domain.Entities; namespace Domain.Repository { public interface IRestaurantRepository { Task<PagedResult<List<Restaurant>>> GetAllRestaurants(GetRestaurantQuery query); // 此处报错,领域层无法访问应用层的GetRestaurantQuery Task<Restaurant> GetRestaurantById(int Id); Task<int> CreateRestaurant(Restaurant restaurant); } }
考虑过两种方案:移除仓储层(但仍想了解正确处理方式);传递原始类型到IRestaurantRepository方法再在应用层构造对象,但觉得不合理。想知道正确的处理方式。
解决方案
根据整洁架构的核心依赖原则(领域层不依赖任何外层),可以采用以下几种合理的处理方式:
1. 在领域层定义查询参数模型
把分页、筛选等查询参数封装成领域层的纯数据模型,比如RestaurantQueryParams,这样领域层的仓储接口可以直接依赖这个模型,应用层的GetRestaurantQuery只需做简单映射即可:
// 领域层的参数模型 namespace Domain.Models { public class RestaurantQueryParams { public int PageIndex { get; set; } public int PageSize { get; set; } // 其他筛选参数,如Name、Category等 } } // 领域层的仓储接口修改为 public interface IRestaurantRepository { Task<PagedResult<List<Restaurant>>> GetAllRestaurants(RestaurantQueryParams queryParams); // 其他方法... } // 应用层Handle方法中完成映射 public async Task<PagedResult<List<RestaurantDto>>> Handle(GetRestaurantQuery request, CancellationToken cancellationToken) { var queryParams = new RestaurantQueryParams { PageIndex = request.PageIndex, PageSize = request.PageSize, // 映射其他筛选参数 }; var restaurants = await _restaurantRepository.GetAllRestaurants(queryParams); // 将领域实体Restaurant映射为应用层的RestaurantDto }
这种方式完全符合整洁架构的依赖方向,既保持了参数的封装性,又避免了领域层对外层的依赖。
2. 直接传递离散原始参数(适合参数较少的场景)
如果查询参数数量不多,可以直接传递单个原始参数到仓储方法,避免额外的模型映射:
// 领域层仓储接口 public interface IRestaurantRepository { Task<PagedResult<List<Restaurant>>> GetAllRestaurants(int pageIndex, int pageSize, string? nameFilter = null); // 其他方法... } // 应用层Handle方法调用 public async Task<PagedResult<List<RestaurantDto>>> Handle(GetRestaurantQuery request, CancellationToken cancellationToken) { var restaurants = await _restaurantRepository.GetAllRestaurants(request.PageIndex, request.PageSize, request.NameFilter); // 后续DTO映射逻辑 }
这种方式简洁直接,但参数过多时会降低代码可读性和可维护性。
3. 定义通用分页参数基类
在领域层定义一个通用的PagedQueryParams基类,所有需要分页的查询参数都继承该基类,实现分页逻辑的复用:
// 领域层通用分页基类 namespace Domain.Models { public class PagedQueryParams { public int PageIndex { get; set; } = 1; public int PageSize { get; set; } = 10; } // 餐厅查询参数继承基类,扩展筛选字段 public class RestaurantQueryParams : PagedQueryParams { public string? Name { get; set; } public string? Category { get; set; } } }
这种方式兼顾了复用性和封装性,适合存在多个分页查询场景的项目。
内容的提问来源于stack exchange,提问作者March3wa
相关产品推荐
相关产品推荐

