[译文]JPA的实施模式:服务门面和数据传输对象

原文:JPA implementation patterns: Service Facades and Data Transfers Objects

作者:Vincent Partington

出处:http://blog.xebia.com/2009/05/11/jpa-implementation-patterns-service-facades-and-data-transfers-objects/

 

在前一篇关于JPA实施模式的博客中,我谈到了DTO和服务门面(Service Façade)模式这一主题,在本篇博客中,我将会探讨为什么我们需要这些模式,甚至会把它们和DAO模式应用到JPA应用架构的更广泛的情形中。

在首次实现JPA代码时,如果说我从中学到了一样东西的话,那就是一些“守旧派”企业应用的架构模式仍然适用,尽管有人已经宣布他们不再是必要的:

Ÿ           DAO已被宣布死亡,因为你只要直接调用EntityManager就可以了,它提供了一个足够好的接口,但从JPA到不同的持久性实现的转换不如使用DAO抽象来得容易。

Ÿ           DTO被认为是多余的,因为你也可以在表现层中直接使用领域对象,视图模式中的打开的EntityManager、显示JSP中的领域对象的标签库和把HTTP请求参数映射回领域对象的数据绑定实用程序的联合使用使这一做法变成可能,

Ÿ           而且最终看来服务门面似乎也已过时,作为替代,你可以让控制器直接调用其所需的服务,或者甚至更简单,直接在其内部包含业务逻辑。

 

由此产生的应用架构看起来就像是这一SpringWeb MVC应用架构图中显示的样子:


[译文]JPA的实施模式:服务门面和数据传输对象
  

那么为什么要关注DAODTO和服务门面呢?

 

我曾经论证过为什么DAO模式在JPA应用架构中仍然是有价值的,类似的理由可以用在DTO和服务门面模式上。虽然前面显示的缺少DTO和缺少服务门面的架构对于简单的Web应用来说可以工作得很好,但是它有两个主要的缺点:

Ÿ           如果你希望把应用暴露给其他的非HMTL客户端(考虑一下使用SOAPweb service、使用AMFFlex前端或者使用JSONAjax应用)的话,那么你会需要一个更明确界定的接口,该接口指定哪些服务被暴露给客户端以及哪些类型被用作输入和输出,服务门面和DTO会各自帮助你定义此接口。

Ÿ           直接在表示层使用领域对象要求这些领域对象使用gettersetter方法来把它们所有的域暴露成公共属性,如果域没有被如此暴露的话,那么标签库将无法呈现它们且数据绑定代码不能设定它们。正如Allen Holub之前已论证的那样,gettersetter方法是有害的。(顺便说一下,有些人把Holub的文章理解为把所有域修饰成公共的的借口,该文认为公共的gettersetter方法不太好,但那并不意味着代码中就只用公共的域,相反,文章建议使用告知而非询问(tell, don’t ask)的面向对象方法。)

 

实际上我发现为什么服务门面和DTO依然有用的原因是因为,我和我的团队正在开发的应用有一个Flex前端和一个与分别使用AMFHessian与核心通信的命令行界面,而应用产生的HTML代码只是为Flex前端加载SWF文件!我们开始的时候就像一般人的做法一样,并未用到DAODTO和服务门面,不过我们把它们全部添加到了我们的架构中以确保其成功,因此作为一个额外的奖赏,我们得到了一个在服务层和表现层之间明确定义的接口。

 

DTO的优缺点

 

当然如果说应该总是在架构中使用DTO那是愚蠢的,一如往常,要视情况而定。J为了让你做出自己的决定,我将列出使用DTO的一些优点和缺点:

Ÿ           缺点:DTO导致代码重复,特别是在DTO与领域对象有着完全相同的域的时候,在两者都为这些域配备了gettersetter方法的时候更是如此,不过在架构中使用DTO可以让你省去领域对象中的gettersetter方法。

Ÿ           缺点:DTO需要你编写来回拷贝属性的样板代码,有些人建议使用诸如DozerApache Commons BeanUtils或者Spring框架的BeanUtils类一类的Java Bean映射框架,不过这需要你把gettersetter方法添加到你的bean中,而我们才刚刚决定我们不想再这样做了!

Ÿ           优点/缺点:DTO使得使用EntityManager.merge来把它们的内容拷贝到你的持久性对象中变为不可能,相反,你不得不使用在我的那篇关于保存(游离的)实体的博客中所描述的DIY合并模式(DIY merge pattern),当然,迫使你以特别的方式,并且不是百分之百令人满意的方式做一些事情,实在不是什么优点,不过至少DTODIY合并之间配合得很好。

Ÿ           优点:DTO确保你不会在表现层中遭遇意想不到的延迟加载问题,或者在远程调用的情况下,它们会使你避免在序列化客户端的传输甚至是未知情况时所产生的延迟加载问题。

Ÿ           优点:DTO模式迫使你考虑应用的接口,你可以把DTO变得比普通的领域对象更为丰富,例如,把安全信息添加到其中,或者把来自多个领域对象的信息聚集起来放到一个DTO中使接口更易于使用。

 

在我们继续讨论服务门面之前,也许应该看看Anirudh Vyas的这篇关于DTO模式的常见不当用法

 

服务门面的优缺点

 

正与DTO的情况类似,在一些情况中服务门面非常有意义,而在一些情况中它们只是增加了无意义的开销,让我们来看看其中的一些优点和缺点:

Ÿ           缺点:服务门面增添了一个额外的层次,该层除了代表实际的服务外并没有做太多的事情,这一理由对于那些有着关于基于我们在本世纪初创立的多层架构的EJB1.0的不好回忆的Java EE开发者来说,是特别的有感触。

Ÿ           优点:服务门面可以(应该!)负责DTO和领域模型之间的来回映射,服务门面在被调用时以DTO为参数,把它们映射到领域模型,调用实际的某个服务(或多个服务),把结果映射回DTO并把这些DTO返回给客户端。实际上,为了确保服务门面只遵守单一责任原则(Single Responsibility Principle),你应该把映射逻辑提出来,放到单独的DTO2DOMapperDO2DTOMapper类中。

Ÿ           优点:服务门面可起到应用的事务边界的作用,即事务在请求到达服务门面期间开始,而不再需要为所有服务定义事务属性,你可以假定所有的服务都通过服务门面来获得调用。实际上,在处理请求期间希望调用多个服务时,或者在想让服务门面把延迟加载的领域对象映射到DTO上的时候,把服务门面设置成事务的边界则是必须要做的事情,在这种情况下,你最终得到的是视图模式中的打开的EntityManager之类的对象;启动的事务从把进来的DTO转换成领域对象这一刻开始,一直持续到把最终产生的DTO返回给客户端这一刻为止。(顺便说一下,如果你希望强制通过服务门面来调用所有的服务的话,那么你可以把这些服务的事务属性设置为MANDATORY。当这样的一个服务被调用时,如果事务还没有被启动,就会有异常抛出。)

Ÿ           优点:服务门面会迫使你思考应用的接口,而不是允许客户端访问你所有的服务,你可以决定最终暴露哪些。

 

嘿,我好像还没有想到服务门面模式太多的缺点,除了“开销”这一说法之外,不过这是一种较主观的看法。请不要迟疑,随时在评论部分中添加更多的关于这一模式的缺点。

 

对应用架构的影响

 

如果应用DAODTO和服务门面模式的话,我们最终得到的JPA应用架构看起来会是这样子的:

 



[译文]JPA的实施模式:服务门面和数据传输对象
 
  

当有请求产生时,事件是以这样的顺序发生的:

1.         服务的客户端发送请求到服务门面,发送的所有对象都是DTO

2.         开始一个事务。

3.         服务门面调用DTO2DO映射器把进来的DTO映射到领域对象上,DTO2DO映射器可以调用一个或多个DAO来从数据库中加载领域对象。

4.         服务门面调用一个或多个服务来执行实际的业务逻辑。

5.         服务门面把返回值传递给DO2DTO映射器并得到返回的DTODO2DTO映射器可以调用一个或多个服务或DAO来给DTO添置内容。

6.         提交事务,或者在异常抛出的情况下回滚事务。

7.         服务门面把DTO传递给客户端。

8.         服务的客户端接收DTO

 

实际上,如果把图中的“DTO2DO映射器”替换成“数据绑定”,把“DO2TDO映射器”替换成“视图呈现”,以及把“服务门面”替换成“前端控制器”的话,那么你就会得到我们一开始时提到的原始的Web MVC架构,其最大的不同在于进来的“DTO”是请求参数而出去的“DTO”是HTML

 


[译文]JPA的实施模式:服务门面和数据传输对象
  

这使得本篇博客回到了其开始的地方,所以现在是时候来个最后的总结了。正如你所见到的那样,在是否使用DTO和服务门面这个问题上,并没有什么明确的答案,这完全取决于你要实现的应用是什么样的,比如说:

Ÿ           它是一个普通的HTML应用还是你希望通过不同的协议来暴露它?

Ÿ           你希望客户端和服务端之间的耦合程度是怎样的?

Ÿ           你希望领域对象不带有任何的gettersetter方法吗?

 

我很有兴趣听一听关于这些模式在当前的Java EE架构中的有效性你们是怎么想的?在什么情况下会使用它们?或者哪些情况下不会使用它们以及为什么?我们在下篇博客中再见,下次我会尝试解决如何在JPA中处理继承这一问题。

 

附:上周我给info.nl的一帮家伙就这一主题做了一个介绍,他们给我提供了一些很有意思的反馈意见,对本篇博客的形成很有帮助,感谢他们!

 

你可能感兴趣的:(DAO,应用服务器,jpa,Flex,企业应用)