实体到DTO的使用


15

一直在尝试提出基本的分层Web应用程序的流程,并一直在线阅读有冲突的信息。我想弄清楚的是,通过使用某种映射器,仍然可以从DAO到Service层继续使用DTO对象是否有优势。

我预见的基本流程如下:

  1. UI模型/表单->控制器
  2. 控制器将模型转换为域对象(实体)
  3. 域对象->服务层
  4. 域对象-> DAO
  5. DAO->域对象
  6. 服务-> UI
  7. UI将Domain转换为UI模型

如果遵循DTO,则DAO将回传DTO,而不是实体。经过一番阅读后,由于(至少在Java中)实体已成为带注释的POJO,因此DTO似乎已经失效,这意味着它们的内存占用量已经很小。

是这种情况,还是应该使用DTO将域对象完全封装在DAO层中?如果是这种情况,服务层将如何传递给DAO?

谢谢一群!

Answers:


20

根据我的说法,传递持久性POJO(例如由JPA管理的bean)不是一种好习惯。

为什么?

我看到三个主要原因:

  1. 延迟集合的潜在问题。http://java.dzone.com/articles/avoid-lazy-jpa-collections
  2. 实体应包含行为(与Anemic域模型相反),您可能不想让UI调用某些意外行为。
  3. 对于贫乏的领域模型,您可能不想将模型结构暴露给UI,因为对模型的每次新更改都可能破坏UI。

我更喜欢让我的服务层在两个方向上将实体转换为相应的DTO。DAO仍是返回实体(确保转换不是它的工作)。


因此,如果我正确理解这一点,那么该服务实际上仅处理DTO对象,并充当UI和DAO的中介。同样,在第3点上,您仍然必须将DTO转换为可使用的UI元素,因此,由于DTO也需要更新,因此域更新不会仍然中断UI。
dardo

1
@dardo UI元素是DTO,或者在最坏的情况下,应在调用服务器端的某些服务之前将其转换为DTO。DTO不太可能经常更改,只是您的实体针对UI需求进行了调整。此外,服务层应该同时关心:DTO和实体。
Mik378

啊,好吧,这是我不了解的问题。贫血症域模型在我工作的地方很常见,并且我试图稍微改变这种模式以鼓励使用更薄的服务层。再次感谢!
dardo

@dardo您可以阅读这本书。:)伟大的伟大的书(或展示贵公司)amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/...
Mik378

实际上在kindle上找到了这个,amazon.com
Domain

13

我认为这一讨论反复出现的原因之一是,将一个对象与您需要的所有数据一起转换为与该对象看起来相同或几乎相同的对象似乎是一件痛苦的事。你正在交接。

是的,这是PITA。但是这样做有几个原因(除了上面列举的原因之外)。

  • 域对象可能变得很重,并且包含许多无用的信息供调用。由于所有传输,整理/解组和解析的数据,这种膨胀会使UI变慢。当您考虑到FE将有许多链接指向您的Web服务并被AJAX或其他多线程方法调用时,您很快就会使UI呆滞。所有这些都可以提高Web服务的总体可扩展性
  • 公开太多数据很容易损害安全性。如果您不从DTO结果中删除用户的电子邮件地址和电话号码,则至少可以公开它们。
  • 实际考虑:对于1个要作为持久域对象和DTO进行游行的对象,它必须具有比代码更多的注释。当对象穿过层时,在管理对象状态时会遇到许多问题。通常,这是PITA的管理要多得多,然后只需执行将字段从域对象复制到DTO的繁琐工作即可。

但是,如果将转换逻辑封装到转换器类的集合中,则可以相当有效地进行管理。

看看lambdaJ,您可以在其中执行“ convert(domainObj,toDto)”操作,其中有很多用于集合的方法。这是一个使用它的控制器方法的示例。如您所见,它看起来还不错。

    @GET
    @Path("/{id}/surveys")
    public RestaurantSurveys getSurveys(@PathParam("id") Restaurant restaurant, @QueryParam("from") DateTime from, @QueryParam("to") DateTime to) {

        checkDateRange(from, to);

        MultiValueMap<Survey, SurveySchedule> surveysToSchedules = getSurveyScheduling(restaurant, from, to);
        Collection<RestaurantSurveyDto> surveyDtos = convert(surveysToSchedules.entrySet(), SurveyToRestaurantSurveyDto.getInstance());
        return new RestaurantSurveys(restaurant.getId(), from, to, surveyDtos);

    }

感谢您的输入,我的想法也一样=)
dardo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.