在处理非幂等调用时,我应该使用事件设计吗?

时间:2013-07-31 04:18:44

标签: domain-driven-design

我正在进行一个航空预订项目。

下图显示了我们目前开发的域模型。

enter image description here

我们定义了一个域名服务(AirBookService),它封装了预订,票务和其他操作。我们的供应商提供Remote-Procedure-Call api来处理这些请求,因此我们通过添加反腐败层(我们有多个供应商)来实现域服务。

这个解决方案在处理imdenpotent rpc调用(例如获取价格)时工作正常。但是,在处理非imdenpotent rpc调用时存在风险。

例如

public class TransactionalReservationHandlingServiceImpl .... {
    @Transactional
    @Override
    public void modifyTraveler(String resId, String tktId, AirTravler traveler) {
         AirReservation res = reservationRepository.findBy(resId);
         res.modify(tktId, traveler);
         airBookService.modify(res, traveler);
         reservationRepository.store(res);
    }
}

我将airBookService.modify()置于res.modify()后面,因此如果某些本地域逻辑被破坏,则可以避免rpc调用。但是如果rpc调用成功并且本地事务失败怎么办?我们的申请中的旅行者与供应商申请中的旅行者之间存在差异。

是否值得在单独的交易中处理rpc调用和本地修改?

我担心的是:

a) It will surely introduce some extra complexity if doing so. like messaging.
b) I don' have much experience in event handling.
c) The failure chances are very low even if we use rpc call in the transaction boundary, mostly caused by concurrency problem and contetion of AirReservation is relatively low in real world.

以下是我的活动尝试:

    @Transactional
    @Override
    public void modifyTraveler(String resId, String tktId, AirTravler traveler) {
         AirReservation res = reservationRepository.findBy(resId);
         ModifyTravelerEvent event = airBookService.modify(res, traveler);
         handlingEventRepository.store(event);
         events.notifyTravelerModified(event);// using messaging
    }

    @Transactional
    @Override
    public void modifyTraveler(String eventSequence) {
         ModifyTravelerEvent event = handlingEventRepository.of(eventSequence);
         AirReservation res = reservationRepository.findBy(resId);
         event.handle(res);
         reservationRepository.store(res);
         handlingEventRepository.store(event );
    }

优点是本地修改与rpc调用分开。 但是这引入了: 1.多资源管理问题(数据源和消息传递) 2.我必须为修改旅行者,需求票据和任何其他AirBookService操作创建大量的临时事件。

我处于两难境地,对目前的设计不满意,但对新的活动设计犹豫不决。

感谢任何想法,提前谢谢。

1 个答案:

答案 0 :(得分:0)

在第一个示例中,您将本地修改与远程修改混合在一起。您担心如果远程修改成功后本地修改失败,则无法再回滚远程修改。所以取消混合你的两个修改绝对是要走的路。

最简单的方法是交换行airBookService.modify调用和reservationRepository.store调用:

public class TransactionalReservationHandlingServiceImpl .... {
    @Transactional
    @Override
    public void modifyTraveler(String resId, String tktId, AirTravler traveler) {
         // local modification
         AirReservation res = reservationRepository.findBy(resId);
         res.modify(tktId, traveler);
         reservationRepository.store(res); // <- remember this should not actually store anything until after the commit

         // remote modification
         airBookService.modify(res, traveler);
    }
}

由于您的本地修改是事务性的,因此只有在成功进行远程修改后才会提交。可能已经发生过任何可能发生的本地问题。当然,提交交易本身失败的可能性仍然微乎其微。因此,要真正的事务性,您必须能够回滚远程修改。因为,我认为,你无法做到这一点,真正的交易性实际上是不可能的。因此,就一致性进行本地和远程修改而言,上述构造是最安全的可能方式,因为本地修改本身的提交失败的可能性可以忽略不计。我这样说,因为即使你要引入消息传递,在远程修改之后仍然存在类似的消息本身未提交的可能性。

上面的结构确实有一个大问题:它可能会严重妨碍性能(你不希望你的事务延迟太久)。因此,消息传递是解决此问题的非常合理的解决方案。它还具有其他优点,如持久性,审计和重播消息。因此,您的消息传递尝试在这方面非常合法。但是,您的代码确实严重违反了单一责任规则,因为消息传递本身与相同方法调用中的修改混合在一起。

如果你担心消息传递过多的样板,你一定要看看akka.io. Spring JMS和ActiveMQ也是一个非常强大的组合,如果你不是在寻找一个完整的范例转换,而只是为了一个体面的消息传递解决方案。使用这两种技术中的一种,我建议你可以为本地调用创建一个强大的框架,与远程调用配对,可以避免大量的样板。

我希望这会有所帮助。祝你好运!