700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 设计模式 面向对象八:如何利用基于充血模型的DDD开发一个虚拟钱包系统?

设计模式 面向对象八:如何利用基于充血模型的DDD开发一个虚拟钱包系统?

时间:2018-12-23 19:36:19

相关推荐

设计模式 面向对象八:如何利用基于充血模型的DDD开发一个虚拟钱包系统?

钱包业务背景

一般具有支付、购买功能的应用,都支持钱包的功能。应用为每个用户开设一个系统内的虚拟钱包账户,支持用户重置、提现、支付、冻结、透支、转账、查询账户余额、查询交易流水。一般来讲,每个虚拟钱包账户都会对应用户的一个真实的支付账号,有可能是银行卡账户,也有可能是三方支付账户(支付宝、微信钱包)。

钱包的五大核心功能的业务

充值

用户通过三方支付渠道,把自己银行卡账户内的钱,充值到虚拟钱包账户。

流程: 第一是从用户的银行卡账户转账到应用的公共银行卡账户第二是将用户的充值金额加到虚拟钱包余额上第三是记录刚刚这笔交易流水

支付

用户用钱包内的余额,支付购买应用内的商品,支付的过程就是个转账的过程。

流程: 第一 从用户的虚拟钱包账户划钱到商家的虚拟钱包账户上第二 触发真正的银行转账操作,从应用的公共银行账户转钱到商家的银行账户(注意,这儿并不是从用户的银行账户转到商家的银行账户)第三 记录这笔支付的交易流水

提现

用户可以将虚拟钱包中的余额,提现到自己的银行卡中。

流程: 第一 从用户的虚拟钱包账户金额减去提现的金额第二 触发银行转账操作,从应用的公共银行卡赚钱到用户的银行卡第三 记录这笔支付的交易流水

查询余额

用户查询虚拟钱包的余额数字即可查询交易流水

用户根据条件查询之前记录的交易即可

钱包系统的设计思路

根据上面的业务流程,我们可以把钱包系统的业务 划分为两部分

一部分单纯的跟应用内的虚拟钱包账户打交道一部分单纯的跟银行账户打交道

基于这样的业务划分,给系统解耦,将整个钱包系统拆分为两个子系统:虚拟钱包系统和三方支付系统

下面代码只聚焦于虚拟钱包系统的设计和实现。 基于贫血模型的传统开发模式

public class VirtualWalletController {// 通过构造函数或者 IOC 框架注入private VirtualWalletService virtualWalletService;public BigDecimal getBalance(Long walletId) { ... } // 查询余额public void debit(Long walletId, BigDecimal amount) { ... } // 出账public void credit(Long walletId, BigDecimal amount) { ... } // 入账public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) { ...} // 转账}public class VirtualWalletBo {// 省略 getter/setter/constructor 方法private Long id;private Long createTime;private BigDecimal balance;}public class VirtualWalletService {// 通过构造函数或者 IOC 框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWalletBo getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWalletBo walletBo = convert(walletEntity);return walletBo;}public BigDecimal getBalance(Long walletId) {return virtualWalletRepo.getBalance(walletId);}public void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();if (pareTo(amount) < 0) {throw new NoSufficientBalanceException(...);}walletRepo.updateBalance(walletId, balance.subtract(amount));}public void credit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();walletRepo.updateBalance(walletId, balance.add(amount));}public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactionEntity();transactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setFromWalletId(fromWalletId);transactionEntity.setToWalletId(toWalletId);transactionEntity.setStatus(Status.TO_BE_EXECUTED);Long transactionId = transactionRepo.saveTransaction(transactionEntity);try {debit(fromWalletId, amount);credit(toWalletId, amount);} catch (InsufficientBalanceException e) {transactionRepo.updateStatus(transactionId, Status.CLOSED);...rethrow exception e...} catch (Exception e) {transactionRepo.updateStatus(transactionId, Status.FAILED);...rethrow exception e...}transactionRepo.updateStatus(transactionId, Status.EXECUTED);}}

基于充血模型的DDD开发模式

public class VirtualWallet { // Domain 领域模型 (充血模型)private Long id;private Long createTime = System.currentTimeMillis();;private BigDecimal balance = BigDecimal.ZERO;public VirtualWallet(Long preAllocatedId) {this.id = preAllocatedId;}public BigDecimal balance() {return this.balance;}public void debit(BigDecimal amount) {if (pareTo(amount) < 0) {throw new InsufficientBalanceException(...);}this.balance.subtract(amount);}public void credit(BigDecimal amount) {if (pareTo(BigDecimal.ZERO) < 0) {throw new InvalidAmountException(...);}this.balance.add(amount);}}public class VirtualWalletService {// 通过构造函数或者 IOC 框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWallet getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);return wallet;}public BigDecimal getBalance(Long walletId) {return virtualWalletRepo.getBalance(walletId);}public void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.debit(amount);walletRepo.updateBalance(walletId, wallet.balance());}public void credit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.credit(amount);walletRepo.updateBalance(walletId, wallet.balance());}public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {//... 跟基于贫血模型的传统开发模式的代码一样...}}

这种充血模型的设计思路,看起来领域模型VitualWallet类很单薄,包含的业务逻辑很简单,这种思路貌似没太大优势。

这也是大部分业务系统都使用基于贫血模型开发的原因。

不过当虚拟钱包需要支持更复杂的业务的时候,它的优势就会显现出来。

比如要支持透支一定额度和冻结部分余额的功能,这是重新设计VirtualWallet类的实现代码

public class VirtualWallet {private Long id;private Long createTime = System.currentTimeMillis();;private BigDecimal balance = BigDecimal.ZERO;private boolean isAllowedOverdraft = true;private BigDecimal overdraftAmount = BigDecimal.ZERO;private BigDecimal frozenAmount = BigDecimal.ZERO;public VirtualWallet(Long preAllocatedId) {this.id = preAllocatedId;}public void freeze(BigDecimal amount) { ... }public void unfreeze(BigDecimal amount) { ...}public void increaseOverdraftAmount(BigDecimal amount) { ... }public void decreaseOverdraftAmount(BigDecimal amount) { ... }public void closeOverdraft() { ... }public void openOverdraft() { ... }public BigDecimal balance() {return this.balance;}public BigDecimal getAvaliableBalance() {BigDecimal totalAvaliableBalance = this.balance.subtract(this.frozenAmount);if (isAllowedOverdraft) {totalAvaliableBalance += this.overdraftAmount;}return totalAvaliableBalance;}public void debit(BigDecimal amount) {BigDecimal totalAvaliableBalance = getAvaliableBalance();if (pareTo(amount) < 0) {throw new InsufficientBalanceException(...);}this.balance.subtract(amount);}public void credit(BigDecimal amount) {if (pareTo(BigDecimal.ZERO) < 0) {throw new InvalidAmountException(...);}this.balance.add(amount);}}

总结

基于充血模型的DDD开发模式跟基于贫血模型的传统开发模式相比,主要区别在Service层。在基于充血模型的开发模式下,我们将部分原来在Service类中的业务逻辑移动到了一个充血的Doamin领域模型中,让Service类的实现依赖这个Domain类在基于充血模型的DDD开发模式下,Service类并不会完全移除,而是负责一些不适合放在Domain类中的功能。比如,负责和Repository层打交道、跨领域模型的业务聚合功能、幂等事务等非功能性的工作基于充血模型的DDD开发模式跟基于贫血模型的传统开发模式相比,Controller层和Repository层的代码基本上相同。 这是因为REpository层的Entity生命周期有限,Controller层的VO只是单纯作为一种DTO。两部分的业务逻辑都不会太复杂。业务逻辑主要集中在Service层。所以,Repository层和Controller层继续沿用贫血模型的设计思路是没有问题

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。