DDD领域驱动设计(二)

一、贫血模型和充血模型

       1.1、贫血模型

               定义:领域对象里只有get,set方法,所有的业务逻辑都不包含在内,而是放到Business Logic层              

               优点:

    1. 各层单向依赖,结构清楚,易于实现和维护。
    2. 设计简单,底层模型非常稳定。

               缺点:

    1. 领域层domain object部分比较紧密依赖持久化domain logic被分离到Service层,显得不够OO。
    2. Service过重。

                样例:三层架构  。三层架构业务中经常使用基于贫血模式的开发模式。              

Repository + Entity

Service + BO(Business Object)

Controller + VO(View Object)

代码:

 1 /**
 2  * 账户业务对象
 3  */
 4 public class AccountBO {
 5 
 6     /**
 7      * 账户ID
 8      */
 9     private String accountId;
10 
11     /**
12      * 账户余额
13      */
14     private Long balance;
15     /**
16      * 是否冻结
17      */
18     private boolean isFrozen;
19 
20     public String getAccountId() {
21         return accountId;
22     }
23 
24     public void setAccountId(String accountId) {
25         this.accountId = accountId;
26     }
27 
28     public Long getBalance() {
29         return balance;
30     }
31 
32     public void setBalance(Long balance) {
33         this.balance = balance;
34     }
35 
36     public boolean isFrozen() {
37         return isFrozen;
38     }
39 
40     public void setFrozen(boolean isFrozen) {
41         this.isFrozen = isFrozen;
42     }
43 }
44 
45 /**
46  * 转账业务服务实现
47  */
48 @Service
49 public class TransferServiceImpl implements TransferService {
50 
51     @Autowired
52     private AccountService accountService;
53 
54     @Override
55     public boolean transfer(String fromAccountId, String toAccountId, Long amount) {
56         AccountBO fromAccount = accountService.getAccountById(fromAccountId);
57         AccountBO toAccount = accountService.getAccountById(toAccountId);
58 
59         /** 检查转出账户 **/
60         if (fromAccount.isFrozen()) {
61             throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
62         }
63         if (fromAccount.getBalance() < amount) {
64             throw new MyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE);
65         }
66         fromAccount.setBalance(fromAccount.getBalance() - amount);
67 
68         /** 检查转入账户 **/
69         if (toAccount.isFrozen()) {
70             throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
71         }
72         toAccount.setBalance(toAccount.getBalance() + amount);
73 
74         /** 更新数据库 **/
75         accountService.updateAccount(fromAccount);
76         accountService.updateAccount(toAccount);
77         return Boolean.TRUE;
78     }
79 }
View Code

     1.2、充血模型

              定义:数据和对应的业务逻辑被封装到同一个类中。因此充血模型满足OOP。

          优点:

    1. 面对对象,Business Logic复核单一职责,不想在贫血模型里面那样包含所有业务逻辑,太过于沉重。

          缺点:

    1. 逻辑比较含糊。什么样的逻辑应该放到Domain Object中,什么样的业务逻辑应该放到Business Logic中。

                          切分原则:可重用度高的,和demain object状态密切关联的放到Domain Object中,可重用度低的,和domain object状态没有密切关联的放到Business Logic中。即domain logic只应该和这一个domain object的实例状态有关,而不应该和一批domain object的状态有关。

           代码:

/**
 * 账户业务对象
 */
public class AccountBO {

    /**
     * 账户ID
     */
    private String accountId;

    /**
     * 账户余额
     */
    private Long balance;

    /**
     * 是否冻结
     */
    private boolean isFrozen;

    /**
     * 出借策略
     */
    private DebitPolicy debitPolicy;

    /**
     * 入账策略
     */
    private CreditPolicy creditPolicy;

    /**
     * 出借方法
     * 
     * @param amount 金额
     */
    public void debit(Long amount) {
        debitPolicy.preDebit(this, amount);
        this.balance -= amount;
        debitPolicy.afterDebit(this, amount);
    }

    /**
     * 转入方法
     * 
     * @param amount 金额
     */
    public void credit(Long amount) {
        creditPolicy.preCredit(this, amount);
        this.balance += amount;
        creditPolicy.afterCredit(this, amount);
    }

    public boolean isFrozen() {
        return isFrozen;
    }

    public void setFrozen(boolean isFrozen) {
        this.isFrozen = isFrozen;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public Long getBalance() {
        return balance;
    }

    /**
     * BO和DO转换必须加set方法这是一种权衡
     */
    public void setBalance(Long balance) {
        this.balance = balance;
    }

    public DebitPolicy getDebitPolicy() {
        return debitPolicy;
    }

    public void setDebitPolicy(DebitPolicy debitPolicy) {
        this.debitPolicy = debitPolicy;
    }

    public CreditPolicy getCreditPolicy() {
        return creditPolicy;
    }

    public void setCreditPolicy(CreditPolicy creditPolicy) {
        this.creditPolicy = creditPolicy;
    }
}


/**
 * 入账策略实现
 */
@Service
public class CreditPolicyImpl implements CreditPolicy {

    @Override
    public void preCredit(AccountBO account, Long amount) {
        if (account.isFrozen()) {
            throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
        }        
    }

    @Override
    public void afterCredit(AccountBO account, Long amount) {
        System.out.println("afterCredit");
    }
}

/**
 * 出借策略实现
 */
@Service
public class DebitPolicyImpl implements DebitPolicy {

    @Override
    public void preDebit(AccountBO account, Long amount) {
        if (account.isFrozen()) {
            throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);
        }
        if (account.getBalance() < amount) {
            throw new MyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE);
        }
    }

    @Override
    public void afterDebit(AccountBO account, Long amount) {
        System.out.println("afterDebit");
    }
}

/**
 * 转账业务服务实现
 */
@Service
public class TransferServiceImpl implements TransferService {

    @Resource
    private AccountService accountService;
    @Resource
    private CreditPolicy creditPolicy;
    @Resource
    private DebitPolicy debitPolicy;

    @Override
    public boolean transfer(String fromAccountId, String toAccountId, Long amount) {
        AccountBO fromAccount = accountService.getAccountById(fromAccountId);
        AccountBO toAccount = accountService.getAccountById(toAccountId);
        fromAccount.setDebitPolicy(debitPolicy);
        toAccount.setCreditPolicy(creditPolicy);

        fromAccount.debit(amount);
        toAccount.credit(amount);
        accountService.updateAccount(fromAccount);
        accountService.updateAccount(toAccount);
        return Boolean.TRUE;
    }
}
View Code

          1.3、总结

                   贫血模型在传统开发模式更受欢迎。充血模型用于更加复杂的业务系统,更具优势。

 二、领域模型代码结构

 

        2.1、OOP充血模型实现方案

    1. 将常规Service中代码逻辑,用OOP思想拆分出对象的行为逻辑封装到对象中。
    2. 将常规Service中的代码逻辑,拆分数据查询逻辑封装到Repository资源库。
    3. 将外部服务调用抽象接口,实现拆分到南向网关。
    4. 将消息等抽象接口,实习前拆分到基础服务。
    5. Service拆分后的代码逻辑为领域对象交互,南向网关调用,基础服务调用等。

       2.2、领域模型不适用于复杂查询。列表查询还是直接查询DB,不通过demain。

       2.3、领域对象作为聚合,直接加载子对象问题较多,所有资源库要集成在领域对象中。

       2.4、代码结构                  

                

          

 

          

 

posted on 2024-03-05 10:46  木乃伊人  阅读(19)  评论(0编辑  收藏  举报

导航