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

使用DTO与DAO架构是否需要Entity?三者最佳实践及结构优化问题

现有架构存在的问题

  • 职责边界混乱:DTO的核心作用是接口层数据传输载体,仅应该包含调用方需要的字段,而DAO是和数据库交互的组件,直接绑定DTO相当于把数据库结构和对外接口强耦合,一旦接口要调整字段、或者数据库表结构变更,两边会互相干扰。比如你要给接口加一个返回的统计字段,就得修改DAO关联的DTO,甚至可能误改数据库映射逻辑。
  • 扩展性差:如果后续有多个端(比如移动端、管理后台端)需要不同的Customer返回字段,你要么得维护N个不同的DTO同时调整DAO层的映射逻辑,要么在一个DTO里塞所有端需要的字段,造成冗余还可能泄露敏感字段(比如客户身份证号本来只有管理后台能查看,结果通用DTO带了字段,移动端接口不小心返回)。
  • 业务逻辑无落脚处:现在你把DTO直接当数据库映射对象用,所有业务逻辑只能塞到Service里,时间长了Service会越来越臃肿,不符合面向对象设计,比如Customer本身有「判断是否为高价值客户」的逻辑,总不能每次都在各个Service里重复写一遍。
  • 现有代码还存在明显bug:getCustomerListByUserID的实现里参数名写错,方法接收的是userID,调用的时候传了不存在的customerID。

是否需要定义Customer Entity?

非常有必要。Entity是和数据库表结构一一对应的领域模型对象,字段完全和数据库表字段对齐,只在DAO、Service层流转,不会对外暴露,可以彻底把对外接口和底层数据库实现解耦。

优化方案

可以按分层逻辑调整调用链路:接口层(Resource)接收/返回DTO <-> 服务层(Service)完成DTO和Entity的转换、执行业务逻辑 <-> DAO层仅操作Entity
调整后的核心代码示例:
首先定义Customer Entity:

// 数据库映射实体,仅在服务内部流转
public class Customer {
    private Integer id;
    private String userId;
    private String name;
    private String phone;
    // 其他和数据库表对应的字段、getter/setter、领域方法
    public boolean isHighValueCustomer() {
        // 通用业务逻辑写在这里,不用分散在各个Service
    }
}

调整DAO层为操作Entity:

public interface CustomerDAO extends BaseDAO<Customer> {
    List<Customer> getCustomerListByUserID(String userID);
    void deleteCustomer(Integer customerID);
    void updateCustomer(Customer customer);
}

Service层完成对象转换和业务逻辑:

@Service
public class InternalService {
    @Autowired
    private CustomerDAO customerDAO;
    @Autowired
    private Convertor convertor; // 可使用MapStruct等工具自动完成对象转换

    public List<CustomerDTO> getCustomerListByUserID(String userId) {
        List<Customer> customers = customerDAO.getCustomerListByUserID(userId);
        // 此处可扩展业务逻辑,比如过滤无效客户、计算客户标签等
        return customers.stream().map(convertor::toCustomerDTO).toList();
    }

    public void updateCustomer(CustomerDTO dto) {
        Customer customer = convertor.toCustomerEntity(dto);
        // 前置业务校验,比如判断客户状态是否允许修改
        if (!customer.isHighValueCustomer()) {
            throw new BizException("普通客户暂不允许修改信息");
        }
        customerDAO.updateCustomer(customer);
    }
}

Resource层无需调整,仍保持接收和返回DTO即可。

DTO、DAO、Entity三者最佳实践

  • Entity:仅和数据库表结构绑定,字段一一对应,存放核心领域业务逻辑,仅在DAO、Service层内部流转,禁止直接返回给外部调用方。
  • DTO:仅作为接口层(对外提供服务、调用第三方服务)的数据传输载体,根据接口实际需要定义字段,可以有多个不同的DTO对应不同的接口场景,不包含任何业务逻辑,也不和数据库结构绑定。
  • DAO:数据访问层组件,仅操作Entity对象,核心职责是完成数据库的CRUD,不涉及任何业务逻辑,也不感知DTO的存在。

内容的提问来源于stack exchange,提问作者Hasan Durukan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 01:45:03