在使用轴突的微服务和不使用轴突的微服务之间管理 sagas?

Manage sagas between a microservice that uses axon and one that doesn't?

我正在做一个项目,为了这个问题,有两个微服务:

  1. 一个新的 OrderService(Spring 启动)
  2. “遗留”发票服务(Jersey Web 应用程序)

此外,还有一个 RabbitMQ 消息代理。

在 OrderService 中,我们将 Axon 框架用于事件溯源和 CQRS。

我们现在想使用 sagas 来管理 OrderService 和 InvoiceService 之间的交易。

据我了解,为了进行此更改,我们需要执行以下操作:

  1. SimpleCommandBus 切换 -> DistributedCommandBus
  2. 更改配置以启用分布式命令
  3. 使用 SpringCloud 或 JCloud
  4. 连接微服务
  5. 将 AxonFramework 添加到遗留 InvoiceService 项目并处理收到的 saga 事件。

第四点是我们遇到的麻烦:发票服务由一个单独的团队维护,不愿意进行更改。

在这种情况下,使用消息代理代替命令网关是否可行。例如,不要做这样的事情:

public class OrderManagementSaga {

    private boolean paid = false;
    private boolean delivered = false;
    @Inject
    private transient CommandGateway commandGateway;

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // client generated identifiers
        ShippingId shipmentId = createShipmentId();
        InvoiceId invoiceId = createInvoiceId();
        // associate the Saga with these values, before sending the commands
        SagaLifecycle.associateWith("shipmentId", shipmentId);
        SagaLifecycle.associateWith("invoiceId", invoiceId);
        // send the commands
        commandGateway.send(new PrepareShippingCommand(...));
        commandGateway.send(new CreateInvoiceCommand(...));
    }

    @SagaEventHandler(associationProperty = "shipmentId")
    public void handle(ShippingArrivedEvent event) {
        delivered = true;
        if (paid) { SagaLifecycle.end(); }
    }

    @SagaEventHandler(associationProperty = "invoiceId")
    public void handle(InvoicePaidEvent event) {
        paid = true;
        if (delivered) { SagaLifecycle.end(); }
    }

    // ...
}

我们会做这样的事情:

public class OrderManagementSaga {

    private boolean paid = false;
    private boolean delivered = false;
    @Inject
    private transient RabbitTemplate rabbitTemplate;

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        // client generated identifiers
        ShippingId shipmentId = createShipmentId();
        InvoiceId invoiceId = createInvoiceId();
        // associate the Saga with these values, before sending the commands
        SagaLifecycle.associateWith("shipmentId", shipmentId);
        SagaLifecycle.associateWith("invoiceId", invoiceId);
        // send the commands
        rabbitTemplate.convertAndSend(new PrepareShippingCommand(...));
        rabbitTemplate.convertAndSend(new CreateInvoiceCommand(...));
    }

    @SagaEventHandler(associationProperty = "shipmentId")
    public void handle(ShippingArrivedEvent event) {
        delivered = true;
        if (paid) { SagaLifecycle.end(); }
    }

    @SagaEventHandler(associationProperty = "invoiceId")
    public void handle(InvoicePaidEvent event) {
        paid = true;
        if (delivered) { SagaLifecycle.end(); }
    }

    // ...
}

在这种情况下,当我们在交换中收到来自 InvoiceService 的消息时,我们将在事件网关上或使用 SpringAMQPPublisher 发布相应的事件。

问题:

首先,您指的是 Axon Extensions 以启用分布式消息传递,而不是完全针对您的问题量身定制。虽然这确实是一个选项,但要知道这将需要您配置几个专用于分布式命令、事件和事件存储的单独解决方案。为此使用像 Axon Server 这样的统一解决方案将确保用户不必深入研究三种(或更多)不同的方法来使其全部工作。相反,Axon Server 附加到 Axon Framework 应用程序,它为您完成所有分发和事件存储。

这意味着如果您要使用 Axon 服务器,DistributedCommandBusSpringAMQPPublisher 之类的东西对于实现您的目标是不必要的。


这是一个可以简化您的生活的 FYI;当然不是必须的。那么让我们转到您的实际问题:

Is this a valid approach?

我认为 Saga 以这种形式充当反腐败层是完全可以的。 Saga 声明它对事件做出反应并发送操作。这些操作是以命令的形式还是以其他第三方服务的形式完全取决于您。

请注意,尽管我觉得 AMQP 更像是一种分布式事件解决方案(以广播方式),而不是一种发送命令(到直接处理程序)的方法。它可以根据您的需要进行变形,但我认为它不是命令调度的最佳选择,因为它需要进行调整。

最后,确保您的 Saga 可以处理通过 RabbitMQ 发送这些操作的异常。您不希望 Invoice 服务在该消息上失败,并且让您的 Order 服务的 Saga 认为当然是您的交易在愉快的道路上。

尽管如此,在 Saga 中使用另一个消息代理确实是可行的。

Is there a documented way of handling this kind of scenario in Axon?

目前没有记录的 Axon 方法来处理这种情况,因为没有“一刀切”的解决方案。从纯粹的 Axon 方法来看,使用命令是可行的方法。但正如您所说,由于(不愿意?)其他团队,这不是您所在领域的选项。因此,您可以在不考虑 Axon 的情况下追溯到 Saga 的实际意图,我将总结如下:

  • Saga 管理复杂的业务交易。
  • 对发生的事情(事件)做出反应并发出操作(命令)。
  • Saga 有时间概念。
  • Saga 会随着时间的推移保持状态以了解如何做出反应。

这是我的两分钱,希望这对您有所帮助!