session facade
一个EJB客户端为了完成一个用例需要执行一个商业逻辑。
EJB客户端怎样才能在一个事务(transaction)和一个大批
(bulk)网络调用中执行一个用例的商业逻辑呢?
为了执行一个典型的用例的商业逻辑,多个服务器端对象
(如session或entity bean)通常需要被存取和可能的修
改。问题是session和entity bean的多个细粒度(fine-
grained)调用增加了多次网络调用(而且可能是多个事务
的)的网络开销(overhead),并且导致可维护性差的代码,
因为数据存取和工作流/商业逻辑在客户端之间分散了。
考虑一个在线银行情景,一个servlet接受请求来从一
个账户向另一个账户传输存款,作为一个Web客户端。
在这个情景下(像图1.1所示),一个servlet必须检查
来保证用户是授权了的,从一个银行账户entity bean
取出存款,并且把它存入另外一个银行账户entity bean。
当执行一个entity bean的home和remote interface,
这个方法在重负荷时将无法伸缩(scale),因为整个
情景(scenario)需要至少6个网络调用:3个寻找合适
的entity bean,还有三个实际的传送存款。还有,因为
entity bean是事务性的生物,每个对实体的调用需要
一个分离的服务器端的事务,需要通过应用服务器的
数据存储和维护来和远程实体同步。
更糟的是这个方法无法保证客户的钱的安全性。如果在
存钱时出错了,客户的钱将已经被取出,并且他的钱将
会丢失。用户授权检查,取钱,和存钱完全分离的运行,
并且如果存钱失败,取钱将不会回滚(roll back),结果
是不连续的状态。这里的问题是:当entity bean的方法
被直接调用,每个方法调用是一个分离的工作单元,并且
是一个分离的事务。
一个解决方案是在我们的entity bean中加入额外的逻辑,
来在一个单独的客户调用中完成许多操作。这个解决方案
引入了维护问题,因为我们的entity bean层将不能被应
用到许多不同的方式上。如果我们在每次需要性能增强
时增加应用逻辑到我们的entity bean上时,我们的entity
bean将很快变得臃肿和难于理解,维护和复用。我们有
效的将我们的应用逻辑(动词)和我们的持久化逻辑(名词)
融合起来,但这是差的应用设计。
另一个方法是为我们的客户端划分一个聚合,通过JTA的
大的事务。这将每个entity bean的方法调用工作在一个
相同的事务之下,在一个"全部或没有"(all-or-nothing)
的风格下。如果存钱失败,那么取钱将会被回滚(roll
back)并且用户的钱将会变得安全。然而,这个改进的
解决方案也有很多缺点:
1.高的网络开销。我们将有6个网络调用,将会降低性能。
(除非我们使用local interface)。
2.差的并发性。如果客户端离服务器很远(如一个applet
或application和远程的EJB系统交互,甚至可能通过
internet或一个防火墙),这个事务将会持续一个很长的
时间。这将导致超额锁定(excess locking),增加冲突
和死锁的可能性,并且降低了其他客户端存取同一个
entity bean实例的并发性。
3.高耦合。我们的客户端直接写一个entity bean的
API,这将使客户端和entity bean紧密的耦合在一起。
如果entity bean层需要在将来变化,我们也必须改变
客户端。
4.差的可复用性。执行“传送存款”的用例的商业逻辑
直接被嵌入到客户端。那么它将被有效的套在了那个
客户端中。其他类型的客户端(Java application,
applet,servlet,等等)不能重用这个商业逻辑。这个
把表示逻辑和商业逻辑混在一起是一个差的为任何
重要的发布的应用设计。
5.差的可维护性。
JTA的使用导致完成事务的中间件逻辑和应用逻辑交织在
一起。通过宣称(declarative)事务分离这两个会更干净,
所以我们能拧(tweak)和调(tune)我们的中间件,而且
不会影响我们的商业规则。
6.差的开发角色的分离。一个通用的在大型项目的实践
把表示层逻辑程序员的开发任务(如serlet和jsp开发者)
和商业逻辑/中间件程序员(EJB开发者)分离开来。如果






