将CQRS应用于信用卡充值(使用AKKA)
Applying CQRS to charging credit Card (using AKKA)
鉴于我对 CQRS 有点困惑,我想进一步了解以下情况:
我有一个 Actor 从用户的信用卡中扣款。为此,它联系执行操作的银行外部服务,获得确认结果。
我想知道如何将其应用于 CQRS?
这里需要写的信息是某个用户被扣了一定的金额。所以生成的事件是Charged(UserID, Card, Amount)。像那样的东西。
问题是我见过的所有例子,尤其是 AKKA,只会在命令被验证后生成事件,这样它就被保存在日志中,并用于更新参与者的状态。 Journal 可以在另一边是红色的,这样就可以在这里创建一个 Reading 视图。
通常,在这些示例中,更新状态函数具有某种程度上执行命令的逻辑,因为该命令直接对应于一天结束时的状态更新。这是典型的 BasketShoping 示例:CreateOrder、AddLineItem。所有的Command,都直接翻译成Event,对应Update state函数的具体代码。
然而在这个例子中,需要实际联系一个外部服务,向用户收费,然后生成一个事件。在update状态下,或者读完journal后,是不能联系外部服务的。这是没有意义的。
因此我想知道,具体是如何完成的,以及何时何地完全符合 CQRS 的精神。如果有人能澄清这一点,那就太好了。
非常感谢,
马塔里
我可以想到两种方法。
首先是一个简单的方法。命令是DoCharge(UserId, Card, Amount)
。收到此命令后,您将调用外部支付服务。如果这已成功完成,您将生成一个事件,Charged(UserId, Card, Amount, TransactionId)
并将其存储在日志中。
现在,当然,这不是完全安全的方法,因为您的 Actor 可能会在将请求发送到支付服务之后,但在它收到并保留成功完成的确认之前崩溃。那么你就有向用户收费两次的风险。为了克服这种风险,您必须使您的支付操作是幂等的。这是如何做的。这个例子是基于经典的"RESTify Day trader"文章。我总结一下。
您需要将付款操作分为两个阶段。在第一个中,支付服务创建一个交易令牌。它只是识别交易,还没有执行任何财务操作。创建后,标识符将由您的服务接收并保存在日志中。
在下一阶段,您将执行与第一阶段标识符相关联的付款。如果你的演员现在在中间失败,而在支付服务端成功执行操作,交易令牌将被标记为已被支付服务处理,并且不会让你向客户收取两次费用。现在,如果您重启失败的 Actor,它会尝试 运行 与现有交易令牌关联的支付,支付服务应该 return 结果类似于 "Already executed" 等。当然最后你也把操作的结果持久化到journal中。
鉴于我对 CQRS 有点困惑,我想进一步了解以下情况:
我有一个 Actor 从用户的信用卡中扣款。为此,它联系执行操作的银行外部服务,获得确认结果。
我想知道如何将其应用于 CQRS?
这里需要写的信息是某个用户被扣了一定的金额。所以生成的事件是Charged(UserID, Card, Amount)。像那样的东西。
问题是我见过的所有例子,尤其是 AKKA,只会在命令被验证后生成事件,这样它就被保存在日志中,并用于更新参与者的状态。 Journal 可以在另一边是红色的,这样就可以在这里创建一个 Reading 视图。
通常,在这些示例中,更新状态函数具有某种程度上执行命令的逻辑,因为该命令直接对应于一天结束时的状态更新。这是典型的 BasketShoping 示例:CreateOrder、AddLineItem。所有的Command,都直接翻译成Event,对应Update state函数的具体代码。
然而在这个例子中,需要实际联系一个外部服务,向用户收费,然后生成一个事件。在update状态下,或者读完journal后,是不能联系外部服务的。这是没有意义的。
因此我想知道,具体是如何完成的,以及何时何地完全符合 CQRS 的精神。如果有人能澄清这一点,那就太好了。
非常感谢,
马塔里
我可以想到两种方法。
首先是一个简单的方法。命令是DoCharge(UserId, Card, Amount)
。收到此命令后,您将调用外部支付服务。如果这已成功完成,您将生成一个事件,Charged(UserId, Card, Amount, TransactionId)
并将其存储在日志中。
现在,当然,这不是完全安全的方法,因为您的 Actor 可能会在将请求发送到支付服务之后,但在它收到并保留成功完成的确认之前崩溃。那么你就有向用户收费两次的风险。为了克服这种风险,您必须使您的支付操作是幂等的。这是如何做的。这个例子是基于经典的"RESTify Day trader"文章。我总结一下。
您需要将付款操作分为两个阶段。在第一个中,支付服务创建一个交易令牌。它只是识别交易,还没有执行任何财务操作。创建后,标识符将由您的服务接收并保存在日志中。
在下一阶段,您将执行与第一阶段标识符相关联的付款。如果你的演员现在在中间失败,而在支付服务端成功执行操作,交易令牌将被标记为已被支付服务处理,并且不会让你向客户收取两次费用。现在,如果您重启失败的 Actor,它会尝试 运行 与现有交易令牌关联的支付,支付服务应该 return 结果类似于 "Already executed" 等。当然最后你也把操作的结果持久化到journal中。