2

我正在做一个机票预订项目。

下图显示了我们迄今为止开发的域模型。

在此处输入图像描述

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

在处理无能的 rpc 调用(例如获取价格)时,此解决方案可以正常工作。但是,在处理非幂等 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) 如果这样做肯定会引入一些额外的复杂性。像消息传递。
b) 我在事件处理方面没有太多经验。
c) 即使我们在事务边界使用 rpc 调用,失败的可能性也很低,主要是由于并发问题和现实世界中 AirReservation 的争用相对较低。

以下是我的活动尝试:

    @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 操作。

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

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

4

1 回答 1

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

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

于 2013-08-10T19:15:53.047 回答