You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity与DTO互转映射器:除Service层外的更优放置位置?

除Service层外,是否存在更优的位置放置Entity与DTO互转的映射器?

我们团队一直将Entity与DTO的互转映射器(Mapper)放在Service层,但渐渐发现这种做法可能不是最优解。

目前的实现中,Service层包含大量以DTO为参数或返回DTO的方法,Controller层直接调用这些方法时流程很顺畅。但当ServiceA需要调用ServiceB的方法时,会出现完全没必要的对象转换操作:

// A SERVICE IMPL
public Integer save(ADto aDto) {
   AEntity aEntity = aMapper.toEntity(aDto);

   BEntity bEntity = aEntity.getBEntity();
   BDto bDto = bMapper.toDto(bEntity);
   bService.save(bDto);
   
   aEntity = aRepository.save(aEntity);
   return aEntity.getId();
}

// B SERVICE IMPL
public Integer save(BDto bDto) {
   BEntity bEntity = bMapper.toEntity(bDto);
   bEntity = bRepository.save(bEntity);
   return bEntity.getId();
}

在这个场景里,以下两次转换完全可以避免:

  • bMapper.toDto(bEntity);
  • bMapper.toEntity(bDto);

为了绕开这个问题,我们不得不给每个Service新增一批“重复”方法:

Integer save(Entity entity)
Integer saveFromDto(Dto dto)
Integer save(List<Entity> entities)
Integer saveFromDtos(List<Dto> dtos)

Entity findById(Integer id)
Dto findByIdToDto(Integer id)
List<Entity> findAll()
List<Dto> findAllToDto()

现在我们考虑了两种重构方案:

方案一:将Mapper迁移至Controller层

Controller层负责处理请求响应,本身就需要知晓返回的数据格式,而Service层可以不用承担DTO转换的职责。

  • 优点:无需为每个Service新增Adapter类
  • 缺点:Controller层会增加额外的转换逻辑

方案二:创建Service的Adapter层并在Controller中使用

Adapter层的核心职责是:

  1. 调用映射器将DTO转换为Entity,再调用接收Entity参数的Service方法
  2. 调用映射器将Entity转换为DTO,再将结果返回给Controller层
  • 优点:Controller层无需新增转换逻辑,保持职责单一
  • 缺点:需要为每个Service单独新增对应的Adapter类

这两种重构思路的核心目标都是让Service层摆脱DTO处理的额外工作,专注于业务逻辑与Entity的操作。

内容的提问来源于stack exchange,提问作者Paul Marcelin Bejan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:25:19