【问题标题】:Implementing event sourcing on credit transaction system在信用交易系统上实现事件溯源
【发布时间】:2017-10-01 23:16:24
【问题描述】:

目前我们有一个系统可以处理信用的贷记和借记。

它将每个操作作为交易事件存储在数据库中,但总而言之,我们仍然更新了一个表示用户当前余额的“credit_bank”表。

          Table "public.credit_bank"
     Column     |          Type          | Modifiers 
----------------+------------------------+-----------
 id             | bigint                 | not null
 user_id        | bigint                 | 
 currency       | character varying(255) | 
 amount         | numeric(40,10)         | not null


                 Table "public.transaction"
      Column      |            Type             | Modifiers 
------------------+-----------------------------+-----------
 id               | bigint                      | not null
 amount           | numeric(40,10)              | not null
 credit_bank_id   | bigint                      | 
 status           | character varying(255)      | not null
 transaction_date | timestamp without time zone | 
 type             | character varying(255)      | not null

由于我们一次处理大量事务,我们不得不在每次更新信用表时锁定它以避免过时问题。

然后我遇到了事件溯源。这种模式适用于这种情况吗?

我真的很陌生,所以如果我有什么问题,请告诉我。

在我的理解中,如果我们使用事件溯源,我们就不需要存储“credit_bank”的状态,而是使用事件来得出状态,或者使用快照。但这如何确保当前余额仍然足够。

另外,如果我们每次都通过处理事件来获取状态,那会不会影响性能?

【问题讨论】:

    标签: design-patterns event-sourcing


    【解决方案1】:

    这种模式适用于这种情况吗?

    这听起来像是我在尝试描述一些极端情况时使用的示例,所以我认为是这样。

    在我的理解中,如果我们使用事件溯源,我们就不需要存储“credit_bank”的状态,而是使用事件来得出状态,或者使用快照。但这如何确保当前余额仍然足够。

    这些交易是来自您的业务模式,还是来自世界某个地方的银行向您报告的事情。因为如果是后者,那么您需要考虑一下“当前”余额意味着什么——现在,在远程银行向您发送交易之后但在该交易获得之前,您正在被查询记录在您的数据库中。余额是否仍然“当前”?

    对于来自“其他地方”的信息,我们通常不会对时间做出原始假设。所以不是“当前余额”,而是“时间 = t 的余额”。或“时间=t1 时的余额,最新可用更新为时间=t2”

    换句话说,您开始包括时间建模,并与业务部门讨论可用延迟是否“足够好”,缩短延迟的成本是什么,的成本是多少> 缩短延迟时间,等等。

    如果我们每次都通过处理事件来获取状态,那会不会影响性能?

    可能是这样。通常,事件溯源与 结合使用;这是另一种延迟权衡——更新模型的责任与查询模型的责任是分开的,需要一些管道将新数据从更新模型复制到查询模型。

    【讨论】:

    • 为了回答您的问题,我提到的交易完全由我们的应用程序控制。话虽如此,我们需要能够准确地查询当前余额,因为我们不希望从用户那里过度提取余额。考虑到这一点,您还会认为事件溯源是理想的吗?
    • 您还提到了 cqrs 的延迟权衡,您能详细说明一下吗?
    • @froi CQRS 的延迟最多可能是几毫秒。我现在我的银行没有问题,可以向我显示数据小时数到一天陈旧。对于 99% 的情况,您可能不会遇到真正的问题。但是您需要确定那些/如果甚至 1ms 也可能成为问题的边缘情况。基本上,您需要评估解决方案的业务影响和潜在的长期成本。
    【解决方案2】:

    由于我们一次处理大量事务,我们不得不在每次更新信用表时锁定它以避免过时问题。

    您可能需要使用数据库事务(我指的不是金融事务)来保持两个表的一致性。这会损害性能和可扩展性。

    然后我遇到了事件溯源。这种模式适合在这种情况下应用吗?

    事件溯源非常适合仅附加系统,所以是的。

    在我的理解中,如果我们使用事件溯源,我们就不需要存储“credit_bank”的状态,而是使用事件来得出状态,或者使用快照。但这如何确保当前余额仍然足够。

    在每个withdrawMoneyFromTheAccount 命令之前,您重播该帐户上的所有金融交易并计算当前余额;然后您将交易金额与该余额进行比较并允许或拒绝交易。

    另外,如果我们每次都通过处理事件来获取状态,那会不会影响性能?

    对于锁定,您应该使用带有version 列的乐观锁定,而不是使用悲观锁定(如事务)。获得了有关性能的真正好处,因为使用事件溯源,您只需要保护一个表,即事件存储,而不是像在您当前的架构中那样必须保持同步的两个表。

    【讨论】:

    • 在重放交易方面,最好的做法是重放一切吗?还是理想使用的快照?
    • @froi 您从没有快照开始,然后在必要时添加它们。所以要避免它们。
    • 只是为了确认我是否理解正确,随着时间的推移,事件会累积,最终它们的数量会达到我认为很难重播的程度。这就是您创建快照的目的?
    • @froi 是的。在那之前你不会。
    猜你喜欢
    • 2015-12-21
    • 1970-01-01
    • 2021-10-01
    • 2013-01-21
    • 1970-01-01
    • 2014-04-23
    • 2019-06-13
    • 2012-08-06
    • 2018-09-18
    相关资源
    最近更新 更多