清洁架构下Node.js应用领域实体部分类型构建与定位咨询
针对Clean Architecture下食谱应用的两个问题解答
1. 优化部分类型构建的方案
以下几种方案可以简化你的部分类型定义和转换流程:
- 利用TypeScript内置工具类型:直接用
Pick/Omit基于完整的RecipeEntity生成部分类型,无需重复定义字段:// 从RecipeEntity中挑选需要的字段 type GetRecipe = Pick<RecipeEntity, 'id' | 'title' | 'summary'> & { ingredients: Pick<IngredientEntity, 'name' | 'quantity'>[]; }; // 或者排除不需要的字段 type GetRecipe = Omit<RecipeEntity, 'createdAt' | 'updatedAt' | 'internalNotes'>; - 复用基础DTO结构:如果有多个类似的部分类型,可以先定义公共基础DTO,再扩展出特定类型:
// 基础公共字段 type RecipeBaseDTO = Pick<RecipeEntity, 'id' | 'title' | 'summary'>; // 带配料的详情DTO type GetRecipeWithIngredients = RecipeBaseDTO & { ingredients: Pick<IngredientEntity, 'name' | 'quantity'>[]; }; // 仅基础信息的列表DTO type RecipeListItem = RecipeBaseDTO; - 引入映射器模式:创建专门的映射函数集中处理实体到DTO的转换,避免在多处重复编写转换逻辑:
// 可以放在用例层的mapper模块,比如/use-cases/recipe/mappers/RecipeMapper.ts export const mapRecipeToGetRecipe = (recipe: RecipeEntity): GetRecipe => { return { id: recipe.id, title: recipe.title, summary: recipe.summary, ingredients: recipe.ingredients.map(ing => ({ name: ing.name, quantity: ing.quantity })) }; }; - 结合GraphQL动态字段选择:利用GraphQL的
info参数获取客户端请求的字段,在数据层只查询需要的字段,同时动态生成返回结构。比如解析请求字段后针对性地映射数据,既减少冗余查询,又能灵活适配客户端需求。
2. GetRecipe接口在Clean Architecture中的合理位置
根据Clean Architecture的依赖倒置原则和分层规则,推荐把GetRecipe放在用例层,具体位置可以是:
/use-cases/recipe/dto/GetRecipe.ts
原因如下:
- 领域层是核心,只负责定义完整的业务实体(
RecipeEntity),不应该依赖外部的输出格式需求。GetRecipe是为了适配客户端的精简数据需求,属于业务用例的输出模型,而非核心业务实体。 - 用例层定义了业务规则的输入输出边界,适配器层(比如你的GraphQL API)依赖用例层的输出模型来构建响应,符合“外层依赖内层”的分层原则。
- 如果后续新增其他适配器(比如REST API),用例层的
GetRecipe可以作为统一的输出标准,各适配器只需做少量适配即可使用,避免重复定义类似的类型。
内容的提问来源于stack exchange,提问作者Dave Weng
相关产品推荐
相关产品推荐

