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

问题描述:

我正在进行空中预订项目。在处理无幂等性调用时,我应该使用事件设计吗?

下图显示了我们到目前为止开发域模型。

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.Multiple资源管理问题(数据源和消息) 2.我必须创造一个修改旅客需求票和任何其他AirBookService操作很多特设的事件。

我左右为难,不满意目前的设计但随着新事件的设计相当犹豫。

任何想法表示赞赏,在此先感谢。

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

这样做将是交换线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也是一个非常强大的组合,如果你不是在寻找一个完整的范式转换,而只是一个体面的消息解决方案。使用这两种技术中的一种,我建议你可以为本地调用创建一个强大的框架,并与远程调用进行配对,从而避免使用大量的样板。

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