文章目录
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
01 | 钱包业务背景介绍
- 虚拟钱包账户,支持用户充值、提现、支付、冻结、透支、转赠、查询账户余额、查询交易流水等操作。
01 | 充值
- 分解为三个主要的操作流程:
- 从用户的银行卡账户转账到应用的公共银行卡账户;
- 将用户的充值金额加到虚拟钱包余额上;
- 记录刚刚这笔交易流水。
02 | 支付
- 流程
- 从用户的虚拟钱包转账到商家的虚拟钱包账户上;
- 从应用的公共银行卡转账到商家银行卡;
- 记录刚刚这笔交易流水。
03 | 提现
- 流程:
- 是扣减用户虚拟钱包中的余额,
- 从应用的公共银行账户转钱到用户的银行账户
- 记录这笔提现的交易流水信息。
04 | 查询余额
- 查看一下虚拟钱包中的余额数字即可。
05 | 查询交易流水
- 将之前记录的交易流水,按照时间、类型等条件过滤之后,显示出来即可。
02 | 钱包系统的设计思路
-
把整个钱包系统的业务划分为两部分,其中一部分单纯跟应用内的虚拟钱包账户打交道,另一部分单纯跟银行账户打交道。
- 虚拟钱包:用户虚拟钱包、商家虚拟钱包
- 三方支付:用户银行卡、商家银行卡、应用公共银行卡
-
如果要支持钱包的五个核心功能,虚拟钱包系统需要实现的操作:
- 加、减、查询余额。
-
交易流水该如何记录和查询?
- 有以下两种记录类型:
- 第一种设计思路更好些。主要是为了解决数据的一致性问题。如果一个流水拆分成两条记录,那么如果出现一方付账成功,一方未成功收账的情况下,那么可利用回滚进行重置信息。
- 有以下两种记录类型:
-
我们是否应该在虚拟钱包系统的交易流水中记录充值、提现、支付类型?
- 虚拟钱包支持的仅仅是余额的加加减减操作,不涉及复杂业务概念,所以不需要记录类型。
-
不在虚拟钱包中记录交易类型,那在用户查询交易流水的时候,如何显示每条交易流水的交易类型呢?
- 可以通过记录两条交易流水信息的方式来解决。
- 用户看的,包含交易类型;系统看的,只有余额的加减。
- 可以通过记录两条交易流水信息的方式来解决。
03 | 基于贫血模型的传统开发模式
- 传统的后端三层开发模式
- Controller中主要调用Service的方法,在Service层中,VirtualWalletBo 只包含数据,不包含任何业务逻辑,业务逻辑集中在 VirtualWalletService 中。
04 | 基于充血模型的 DDD 开发模式
- 基于充血模型的 DDD 开发模式,跟基于贫血模型的传统开发模式的主要区别就在 Service 层,Controller 层和 Repository 层的代码基本上相同。
- 把 VirtualWallet 类设计成一个充血的Domain 领域模型,并且将原来在 Service 类中的中的部分业务逻辑移动到 VirtualWallet 类中,让Service 类的实现依赖 VirtualWallet 类。
- 此例子中,领域模型 VirtualWallet 类很单薄,包含的业务逻辑很简单。不过,如果虚拟钱包系统需要支持更复杂的业务逻辑,那充血模型的优势就显现出来了。比如,我们要支持透支一定额度和冻结部分余额的功能。
05 | 辩证思考与灵活应用
-
在DDD 开发模式中,Service 类的担当职责与功能逻辑?
- Service 类负责与 Repository 交流。
- Service 类负责跨领域模型的业务聚合功能。
- Service 类负责一些非功能性及与三方系统交互的工作。比如幂等、事务、发邮件、发消息、记录日志、调用其他系统的 RPC 接口等。
-
Controller 层和 Repository 层还是贫血模型,是否有必要也进行充血领域建模呢?
- 没有必要。
- Controller 层主要负责接口的暴露,Repository 层主要负责与数据库打交道,这两层包含的业务逻辑并不多,就没必要做充血建模。
06 | 课堂讨论
- tip:domain模型使用充血模型设计,使之具备独立性,而业务无关的vo,po就可以使用贫血模型进行设计,因为不涉及具体复杂业务,如果control层需要调用多个领域模型,则把相关的领域服务组合在一起。