本文揭开一个世纪谜团:在做账户设计时,90%的人都会陷入的困局。账户设计时,业务状态的流转及账务处理往往容易让人困惑。同一笔交易,在“交易中”状态需要记一笔账,到“交易成功”时又要记一笔账,如何在账户中清晰体现这一过程,是设计中的一个难点,很多人容易在此犯错。
设计梳理不清的一个常见场景是,将交易状态混入了账户流水中。当账户与业务状态紧密耦合时,就会变得难以理清。01
如下图所示,是知识星球上一位同学的提问:在“交易中”状态时记一笔收入,计入冻结余额部分,此时看似没有问题。当业务层的交易变成成功时,麻烦就来了,此时要进行“冻结余额”解冻10元到可用余额中,那么“02”流水交易状态变成“交易成功”
问题是,03号流水中的可用余额和冻结余额也需要跟着变化,幸好只有一个03,如果已经新产生了1000万笔流水呢,那数据更新量是巨大的
当业务层的交易状态变为成功时,问题就来了。此时需要将“冻结余额”中的10元解冻至可用余额。随之,“02”号流水的交易状态也会更新为“交易成功”。问题在于,03号流水中的可用余额和冻结余额也需要相应调整。幸好现在只涉及一个03号流水,但如果已经新产生了1000万笔流水,那数据更新的工作量将是巨大的。02
这意味着,对于历史中的非终态交易记账,一旦交易状态发生变化,就会牵连到后续大量的记账都需要跟着调整。
对于并发量较大的业务来说,账务系统可就要累垮了,因为它得不断地、一遍遍地更新大量的历史账户流水。而且,这还只是从“交易中”到“交易成功”的情况。如果交易从“交易中”变为“交易失败”怎么办?如果业务线要新增一个状态怎么办?财务需要数据怎么办?给商户的账单又该怎么处理?状态一直在变,没有稳定的终态,每一秒都可能不同,这样账务就变得不可查、不可信了。03
事实不可变更:账务是对业务已经发生事实的记录,这些事实一旦产生就不能变更,就像流水一样,一旦登记就成为了终态。
非一对一强对应:业务单据和账户单据之间并非必然的一对一关系,而可能是一对多、多对一或多对多的关系。因此,我们不能简单地将交易等同于流水。
理解会计事件:交易的状态变化构成了一次会计事件,而账户关注的是这些会计事件。对于每一次事件,我们都应该记录一笔账,而不是随着交易状态的改变去修改账户的状态。
04
将交易状态从账户中剔除,让账户保持其“纯粹性”。账户不再关注业务状态的流转,而只专注于处理每一次的记账请求,每次记账都是终态。如下图所示。我们将记账类型抽象出“冻结”、“解冻”等更加精确的账务处理类型。因此,一笔交易的状态变化会对应多笔账户流水的记录。在交易进行时,记录的是02类型的“收入”账,金额计入冻结余额。交易成功后,则记录04类型的“解冻”账,此时总余额保持不变,冻结余额减少,可用余额相应增加,如下图所示。如果交易失败了,那么04类型的记账就应该是一笔支出账,用于扣减冻结余额,如下图所示。
05
将业务与账户系统进行隔离,账户系统不关注业务的流转情况,只专注于处理每一次的记账请求。账户系统为业务提供各类记账服务,业务在不同事件发生时,向账户系统发起记账请求。以上只是针对星球提问给出的一种解法。当然,账务实现有多种方案,如流水账、复式记账、客户账、内部账等,每种情况都可以选择不同的实现方案。详见本文:详解账务系统,从入门到精通支付全链路爽文推荐
【入门】一文搞定“支付入门”
【入门】一文搞懂184个支付名词
【全局】88张图,把支付清结算串起来
【全局】1.9万字:支付清算生态
【交易】一文搞懂“交易核心”:交易、订单、账单、支付
【支付】3.5万字:一文搞懂“支付系统”
【清结算】万字:清结算,全局实现原理
【账务】3.5万字详解账务系统,从入门到精通
【线下】支付清结算全链路,2天线下集训营