POJO、DO、DTO、BO、VO 解释

阿里巴巴开发规约的定义

  • POJO(Plain Ordinary Java Object):在本规约中,POJO 专指只有 setter/getter/toString 的简单类,包括 DO/DTO/BO/VO 等。

    • 解释:(Plain Ordinary Java Object) 普通 java 对象。
  • DO(Data Object):专指数据库表,此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。

    • 解释:有的地方也叫做 (Domain Object) 领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
  • PO(Persistent Object):也指数据库表,此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。

    • 解释:(Persistent Object) 持久化对象
  • DAO(Data Access Object):数据访问层,与底层 MySQL、Oracle、Hbase、OceanBase 等进行数据交互。

    • 解释:(Data Access Object) 数据访问对象
  • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。

  • BO(Business Object):业务对象。可以由 Service 层输出的封装业务逻辑的对象。

    • 解释:(Business Object) 业务对象,把业务逻辑封装为一个对象,这个对象可以包括一个或多个其它的对象。
  • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

    • 解释:(View Object) 视图对象,它的作用是把某个指定页面(或组件)的所有数据封装起来。
  • Query:数据查询对象,各层接收上层的查询请求。额外规定:【强制】超过2个参数的查询封装,禁止使用Map类来传输。

示例

以学生档案管理系统为例,我们现在需要做个学生档案管理的后台系统,按照上面的定义,我们能定义出以下一些对象:

  • DB 层:存储层,储存数据

    • 这一层只有数据实体对象 DO,DO 是数据表的映射。在有些文章中 DO 又叫 Bean,Model,Domain,DO,Pojo
    • 以 Student 表为例,这里是 StudentDO,有时候也会省略 DO 直接写 Student,和 Python 中的 model 一样。
  • DAO 层或 Mapper 层

    • 这一层是操纵数据对象 DAO,DAO 主要是用来操作数据实体对象 DO。有的框架中把这层也叫作 Mapper 层。
    • 比如,StudentDAO 是一个提供查询、删除、写入 StudentDO 的接口。
  • DO 进化1:DTO

    • 在开发中,经常需要扩展一些业务和扩展一些功能,如果为了方便直接去修改 DO(bean、model、pojo),在类中增加额外的属性,会破坏类的数据结构。
    • 很多框架比如 hibernate、mybatis 都做了很多这方面处理,比如把一些字段增加一些忽略来代表这个字段不是数据库表的列。也不需要和数据库表打交道。
    • 但是问题也很明显,本来我是一个孑然一身的类,但是为了达到某个功能不得不污染,造成类的污染,为了解决这个问题,引入了 DTO。
  • service 层或 core 层

    • 这一层主要是操作数据实体对象 DTO。
    • 这一层是做业务逻辑处理的,比如查询接口,根据学生学号调用 DAO 层接口获取相应的 Student 信息,之后做一次数据裁剪,只取业务字段,例如版本号、自增id,获取一个 StudentDTO,然后查询学生档案相关的 ProfileDTO,组装成 ProfileBO,作为档案领域模型。
  • DO进化2:产生 VO

    • 现在很多测试工具比如 swagger 可以读取参数所有的属性,对于接口的调用者来说是透明的。他们会觉得是不是需要所有的字段都会用到,从而增加调用过程的认知和沟通成本。
    • 如果将 DTO 的数据不做处理全部返回给接口调用者,如果 DTO 中包含不应该返回给前端的数据,会有可能造成信息泄露。
    • 所以这个时候 VO 诞生了。使用 VO 专门来解决视图层和业务层的参数的接受问题,你可能会说,上面这些问题 DTO 也可以实现这个功能,那为什么还要用 VO 呢?
    • 根据职责单一原则,服务层只负责业务,与具体的表现形式无关,DTO 不应该出现与表现形式的耦合,DTO 定义的是原始数据,VO 再对 DTO 数据进行解释。
      • 比如一个服务同时供多个客户端使用(不同门户),不同客户端对于表现层的要求不同,比如管理端要求显示准确的年龄,而应用端为了保护客户隐私,只需要显示一个年龄段即可。
    • 为什么会存在 Vo?
      • 从开发过程讲,前后端首先会以 vo 和 param 作为返回、传参的协议的定义,再定义协议之前,都没有实际的业务逻辑的处理,也就不会存在 DTO。
      • 从项目的整体考虑,service 层的方法不一定都是 public 方法,也有 private 方法,如果 private 方法也返回 DTO,说明这个 DTO 是不希望提供给前端的,这个时候有些 DTO 不是提供给前端的,有些是给前端的,这样就会乱,没有了隔离性。
      • 从字段的修改来说,service 层的方法是可以共用的,一个 service 方法返回的 DTO,可能会被很多个 controlller 方法使用到,即使目前不会,将来也可能会,DTO 会有很多参数,比如包含了主表信息,子表信息,而传给前端的 vo 只有 DTO 的一部分信息,而且不同请求给前端看到的数据不一样,所以 DTO 是共用的,vo 是个性化的,如果直接把 DTO 提供给前端,将会导致耦合性非常大,一旦一个接口的需求变了,修改了 DTO,增加了一个字段,将会导致接口直接把这个新增的字段返回给了前端,导致(接口输出数据多余,和不安全性)。同理,如果由于某个需求,把dto展示给前端的接口说要删除某一个字段,那么因为这个 DTO 被很多接口引用,一删除就会导致出问题。
  • 业务层 controller 层

    • controller 层接受前端传来的参数,然后调用 service 层,然后根据 service 层返回的结果封装成 VO 返回。

一般的数据传递是,前端传递 VO 给接口(Controller),接口将 VO 转为 DTO 传递给 service,service 将 DTO 分解为 DO,调用领域服务进行调度,然后逆向转为 VO 或者其他的返回结果,传递给前台。

总结

  • 其实不论是 VO,还是 BO/DTO 都是为了保护 DO 不对外暴露的问题,这样不直接操作,而是利用一种扩展的方式从而进行数据的交换,让开发和流程上更加清晰而已。任何东西的选择都是按需选择返回(BO/DTO)和按需传递给服务端(VO)。当然这样也更利于你对代码和项目构建的理解。
  • 另外一句话:如果项目很小,不大,其实也不建议弄这么多层,但是如果是企业级的项目尽量使用,从长远的思考是有利的。
  • 架构不是一层不变,能够解决你当下业务并且持久的发展,这种架构才是最好的架构,而不能去盲目的追求,毕竟这些都是人为指定和规范出来的标准。如果你觉得不合适也可以推翻,也可以改造。但是不能盲目的认为这就是标准

POJO、DO、DTO、BO、VO 解释
https://flepeng.github.io/021-Java-POJO、DO、DTO、BO、VO-解释/
作者
Lepeng
发布于
2024年2月2日
许可协议