六边形架构下API结构问题:跨域DTO数据获取方案咨询
我在API结构设计上遇到了问题。我的API包含Study领域类,它有一个educationLevelId属性,该属性关联AcademicLevel领域类的层级结构。
领域层代码
Study领域
public class Study { private final Long id; private final Long employeeId; private Long educationLevelId; //其他属性和方法 }
AcademicLevel领域
public class EducationLevel { private final Long id; //其他属性和方法 } public class AcademicLevel { private final Long id; private List<EducationLevel> educationLevels; //其他属性和方法 }
应用层代码
Study领域的DTO
在Study领域的findStudiesByEmployeeIdUseCase中需要返回StudyDTO,但StudyDTO需要来自AcademicLevel领域的academicLevelId属性:
public class StudyDTO { private Long id; private Long employeeId; private Long educationLevelId; private Long academicLevelId; //其他属性 }
共享服务定义
为此我创建了共享服务接口,实现放在AcademicLevel领域的应用层:
public class AcademicAndEducationLevelIdDTO { private Long academicLevelId; private Long educationLevelId; //Getters } public interface FindAcademicLevelIdsByEducationLevelIdsService { List<AcademicAndEducationLevelIdDTO> execute(List<Long> educationLevelIds); }
核心问题
该服务实现依赖领域层的AcademicLevelRepository,但根据六边形架构原则,领域层不应依赖应用层,现在陷入了依赖矛盾。
现有可选方案
方案1:泛型仓库方法
在AcademicLevelRepository中定义泛型方法,通过指定类类型返回结果:
<R> List<R> findAcademicLevelIdsBy(List<Long> educationLevelIds, Class<R> clazz);
仓库实现使用entityManager动态映射结果,这样AcademicLevelRepository不会依赖应用层的DTO。
方案2:领域层内部DTO
在领域层创建内部DTO包,让仓库返回领域内部DTO,再映射为应用层的AcademicAndEducationLevelIdDTO。但我不喜欢这个方案,因为按照六边形架构原则,DTO属于应用层。
优化建议与替代方案
推荐方案:领域层定义值对象(Value Object)
既然academicLevelId和educationLevelId的关联是领域内的核心关联关系,可以在AcademicLevel领域层定义一个值对象来承载这对关联:
// 领域层 - AcademicLevel领域 public record AcademicLevelEducationLevelPair(Long academicLevelId, Long educationLevelId) {}
然后让AcademicLevelRepository返回这个值对象列表:
// 领域层 - AcademicLevel领域 public interface AcademicLevelRepository { List<AcademicLevelEducationLevelPair> findAcademicLevelIdsByEducationLevelIds(List<Long> educationLevelIds); }
这样应用层的服务实现可以调用仓库获取领域值对象,再映射为AcademicAndEducationLevelIdDTO,完全符合六边形架构的依赖规则:领域层不依赖应用层,应用层依赖领域层的定义。
方案1的补充分析
方案1的泛型仓库方法虽然能解决依赖问题,但会让仓库接口变得不够清晰,同时泛型返回会降低代码可读性,后续维护成本较高。如果只是临时解决小问题可以用,但长期来看不如值对象方案更符合领域驱动设计的思想。
为什么不选方案2
方案2把DTO放到领域层,违反了六边形架构中"领域层专注于业务逻辑,不关心外部数据格式"的原则,会污染领域层的纯净性,不推荐。
内容的提问来源于stack exchange,提问作者JUAN PABLO SOLARTE HOYOS

