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

六边形架构中服务层修改方法返回DTO的合理性咨询

六边形架构中服务层修改方法返回DTO的合理性咨询

嘿,这问题其实是六边形架构(端口适配器模式)里很常见的边界职责困惑,我来给你捋捋~

首先明确说:你想改成服务方法返回DTO、把接口仅用于依赖注入的思路是合理的,但要注意调整职责边界,别把领域层和应用层的活儿混在一起

先回忆下六边形架构的核心:它的本质是把核心领域逻辑和外部依赖(比如API、数据库)彻底隔离开。其中「端口」分为两类:

  • 驱动端口:外部系统(比如你的API控制器)调用领域逻辑的入口,就是你定义的ICreateProduct这类接口,它们是面向领域的,应该只处理领域模型
  • 被驱动端口:领域逻辑调用外部依赖(比如数据库)的出口,比如数据库访问接口

再看你的代码现状:你当前的ProductService其实更像是一个应用服务——它的职责就是协调领域逻辑,同时处理外部和领域之间的数据转换(也就是领域模型转DTO),这完全是应用服务该干的事儿!

你之前的问题出在:原本应该放在应用层的DTO转换,不小心想塞到领域层的接口里了,导致冲突。现在调整成「应用服务依赖领域接口(用于解耦注入),然后对外暴露返回DTO的方法」,刚好契合六边形架构的设计:

具体调整建议

  1. 让领域层保持纯净:ICreateProduct、IUpdateProduct这些驱动端口,以及它们的实现(真正的领域服务),都只处理Product领域模型,不要出现任何DTO的影子——这样领域逻辑才不会被外部细节污染。
  2. 应用服务承担转换职责:你的ProductService作为应用服务,不需要实现那些领域接口,而是依赖它们的实现,然后在方法里完成「DTO转领域模型」或者「领域模型转DTO」的工作,对外暴露给API控制器调用的方法都返回DTO。

调整后的代码大概是这样:

public class ProductService {
    private final ICreateProduct createProduct;
    private final IUpdateProduct updateProduct;
    private final IRetrieveProduct retrieveProduct;
    private final IDeleteProduct deleteProduct;

    public ProductService(ICreateProduct createProduct, IUpdateProduct updateProduct, IRetrieveProduct retrieveProduct, IDeleteProduct deleteProduct) {
        this.createProduct = createProduct;
        this.updateProduct = updateProduct;
        this.retrieveProduct = retrieveProduct;
        this.deleteProduct = deleteProduct;
    }
    
    public boolean deleteProductById(Long id) {
        return deleteProduct.deleteProductById(id);
    }
    
    public GetAllProductsDTO getAllProducts() {
        List<ProductDto> products = retrieveProduct.getAllProducts().stream()
            .map(ProductMapper::toDto)
            .collect(Collectors.toList());
        return new GetAllProductsDTO(products);
    }
    
    public ProductDto getProductById(Long id) {
        Product product = retrieveProduct.getProductById(id)
            .orElseThrow(() -> new RuntimeException("¡Product not found!"));
        return ProductMapper.toDto(product);
    }
    
    public ProductDto updateProduct(Long id, ProductDto productDto) {
        // 先把外部传来的DTO转成领域模型
        Product product = ProductMapper.toDomain(productDto);
        Product updatedProduct = updateProduct.updateProduct(id, product)
            .orElseThrow(() -> new RuntimeException("¡Product not found!"));
        // 再把更新后的领域模型转成DTO返回
        return ProductMapper.toDto(updatedProduct);
    }
    
    public ProductDto createProduct(ProductDto productDto) {
        Product product = ProductMapper.toDomain(productDto);
        Product createdProduct = createProduct.createProduct(product);
        return ProductMapper.toDto(createdProduct);
    }
}

额外的边界提示

从你提供的项目文件夹结构来看,已经区分了不同层级,继续保持这个划分:

  • DTO放在适配器层(比如api模块),属于外部边界的一部分
  • 领域模型、驱动接口(ICreateProduct等)放在领域层
  • 应用服务放在应用层,作为连接外部和领域的桥梁

这样调整后,既解决了你之前的转换错误问题,又完美契合六边形架构的解耦思想——接口依然用来做依赖注入(解耦应用服务和领域实现),同时领域层不会被外部的DTO污染,职责划分清晰。

备注:内容来源于stack exchange,提问作者Frank2497

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:19:31