ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

银行转账DDD案例

2021-10-19 18:31:42  阅读:344  来源: 互联网

标签:转账 Account double newBalance 案例 amount balance public DDD


银行转账事务脚本实现方式

//两个账号间,转账金额计算逻辑全部在MoneyTransferService中,Account仅仅是数据载体
public class MoneyTransferServiceTransactionScriptImpl implements MoneyTransferService {
private AccountDao accountDao;
  
    private BankingTransactionRepository bankingTransactionRepository;
 

    @Override
    public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {
   Account fromAccount = accountDao.findById(fromAccountId);
    Account toAccount = accountDao.findById(toAccountId);
   double newBalance = fromAccount.getBalance() - amount;
    
    //是否支持透支
        switch (fromAccount.getOverdraftPolicy()) {
     case NEVER:
        if (newBalance < 0) {
          throw new DebitException("Insufficient funds");
        }
                break;
       case ALLOWED:
        if (newBalance < -limit) {
                    throw new DebitException(
                            "Overdraft limit (of " + limit + ") exceeded: " + newBalance);
         }
        break;
    }
       fromAccount.setBalance(newBalance);
        toAccount.setBalance(toAccount.getBalance() + amount);
        BankingTransaction moneyTransferTransaction =
                new MoneyTranferTransaction(fromAccountId, toAccountId, amount);
        bankingTransactionRepository.addTransaction(moneyTransferTransaction);
        return moneyTransferTransaction;
  }
}

 

银行转账DDD实现

public class Account {
  // @Id
  private String id;
  private double balance;
  private OverdraftPolicy overdraftPolicy;
  . . .
  public double balance() { return balance; }
  public void debit(double amount) {
    this.overdraftPolicy.preDebit(this, amount);
    this.balance = this.balance - amount;
    this.overdraftPolicy.postDebit(this, amount);
  }
  public void credit(double amount) {
    this.balance = this.balance + amount;
  }

 //构造器
 public Account() {
  //构造器中根据XX创建透支策略
  if(){
    this.overdraftPolicy = new NoOverdraftAllowed();
  }else{
    
this.overdraftPolicy = new XXXX();
  }
 }
}

透支策略OverdraftPolicy不仅仅是一个Enum了,而是被抽象成包含了业务规则并采用了策略模式的对象。

 

public interface OverdraftPolicy{
  void preDebit(Account account, double amount);
  void postDebit(Account account, double amount);
}

public class NoOverdraftAllowed implements OverdraftPolicy {
  public void preDebit(Account account, double amount) {
    double newBalance = account.balance() - amount;
    if (newBalance < 0) {
      throw new DebitException("Insufficient funds");
    }
  }
  public void postDebit(Account account, double amount) {
} }
public class LimitedOverdraft implements OverdraftPolicy { private double limit; public void preDebit(Account account, double amount) { double newBalance = account.balance() - amount; if (newBalance < -limit) { throw new DebitException("Overdraft limit (of " + limit + ") exceeded: " + newBalance); } } public void postDebit(Account account, double amount) {
} }

Domain Service 只需要调用相关的Entity对象完成业务

public class MoneyTransferServiceDomainModelImpl implements MoneyTransferService {
   private AccountRepository accountRepository;
   private BankingTransactionRepository  bankingTransactionRepository;

  @Override
  public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {
    Account fromAccount = accountRepository.findById(fromAccountId);
    Account toAccount = accountRepository.findById(toAccountId);
    
    fromAccount.debit(amount);
    toAccount.credit(amount);
    BankingTransaction moneyTransferTransaction = new MoneyTranferTransaction(fromAccountId, toAccountId, amount);
    bankingTransactionRepository.addTransaction(moneyTransferTransaction);
    return moneyTransferTransaction;
  }
}

 

对比事务脚本,使用领域模型优势

面向对象

  • 封装:Account 账户相关操作,转出/传入操作封装在Account对象内,
  • 多态:策略模式的OveraftPolicy,提高代码拓展性

业务语义显性化

  • 显性化:将隐式业务逻辑从if...else中抽离出来,用通用语言去写代码,扩展,变成显性概念。举例说明,比如”透支策略“是账户Account的一种属性。使用事务脚本,其含义 

完全淹没在代码中,没有凸显出来

  • 通用语言:“一个团队,一种语言”,将模型作为语言的支柱。确保团队在内部的所有交流中,代码中,画图,写东西,特别是讲话的时候都要使用这种语言。例如账号,转账,透支策略,这些都是非常重要的领域概念,如果这些命名都和我们日常讨论以及PRD中的描述保持一致,将会极大提升代码的可读性,减少认知成本。

 

领域服务

有些领域动作,是一些动词,但是却不属于任何对象。它们代表了领域中的一个重要行为,所以不能忽略它们或简单把它们合并到某个实体或者值对象中。当这样的行为从领域中被识别出来,最佳方式是将它们声明成一个服务。它的作用是为了领域提供相应的功能。

Service通常以一个活动来命名,而不是Entity来命名。以转账为例,转账的这个行为就是一个领域概念,但是是发生在两个账号之间,归属于账号Entity并不合适,因为一个账号Entity没必要去关联他转账的账号Entity。这种情况下使用MoneyTransferDomainService更合适。

识别领域服务,主要看是否满足以下三个特征

  • 服务执行的操作代表了一个领域概念,这个领域概念无法自然的归属于一个实体或者值对象
  • 被执行的操作涉及到领域中的其他对象
  • 操作是无状态的

 

标签:转账,Account,double,newBalance,案例,amount,balance,public,DDD
来源: https://www.cnblogs.com/MadYanYan/p/15396971.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有