回看一下领域模型中的贫血模型

本贴最后更新于 379 天前,其中的信息可能已经时移世改

本文来源回看一下领域模型中的贫血模型

本篇笑点

我要从甲方跳到乙方公司了。

你得知道这篇讲的些什么

本文 1517 字,阅读可能需要 2 分 59 秒

Java Web 开发中最常见的领域模型 - 贫血模式

说明

前几个月和杭州的大妖怪说要做一个 xxxx 项目,其中聊到了领域模型
准备写一篇领域模型回顾篇,基本是针对网络上的总结进行一些细节丰富,去除一些不必要的理解。

贫血模式

项目要采用的是 贫血模型,我们先来看一下贫血模型。
而所谓 贫血模型 就是模型对象之间存在完整的关联(可能存在多余的关联),但是对象除了 get 和 set 方外外几乎就没有其它的方法,整个对象充当的就是一个数据容器。

所有的业务方法都在一个无状态的 Service 类中实现,Service 类仅仅包含一些行为。
这是大多数 JavaWeb 程序采用的最常用开发模型,看完下面的包结构你会更清晰这个模型。

包结构

贫血模型的实现一般包括如下包:

内容
dao 负责持久化逻辑
model 包含数据对象,是 service 操纵的对象
service 放置所有的服务类,其中包含了所有的业务逻辑
facade 提供对 UI 层访问的入口
代码实现

先看 model 包的两个类,Account 和 TransferTransaction 对象,分别代表帐户和一次转账事务。由于它们不包含业务逻辑,就是一个普通的 Java Bean,下面的代码省略了 get 和 set 方法。

/**
 * 账户
 **/
public class Account {  
    private String accountId;  
    private BigDecimal balance;  
  
    public Account() {}  
    public Account(String accountId, BigDecimal balance) {  
        this.accountId = accountId;  
        this.balance = balance;  
    }  
    // getter and setter ....  
  
} 
/**
 * 转账
 **/
public class TransferTransaction {  
    private Date timestamp;  
    private String fromAccountId;  
    private String toAccountId;  
    private BigDecimal amount;  
  
    public TransferTransaction() {}  
  
    public TransferTransaction(String fromAccountId, String toAccountId, BigDecimal amount, Date timestamp) {  
        this.fromAccountId = fromAccountId;  
        this.toAccountId = toAccountId;  
        this.amount = amount;  
        this.timestamp = timestamp;  
    }  
  
    // getter and setter ....  
} 

这两个类很常见,就是数据容器。
接下来看 service 包中 TransferService 接口和它的实现 TransferServiceImpl。TransferService 定义了转账服务的接口,TransferServiceImpl 则提供了转账服务的实现。

public interface TransferService {  
    TransferTransaction transfer(String fromAccountId, String toAccountId, BigDecimal amount)   
            throws AccountNotExistedException, AccountUnderflowException;  
}  
public class TransferServiceImpl implements TransferService {  
    private AccountDAO accountDAO;  
    private TransferTransactionDAO transferTransactionDAO;  
  
    public TransferServiceImpl(AccountDAO accountDAO,   
            TransferTransactionDAO transferTransactionDAO) {  
        this.accountDAO = accountDAO;  
        this.transferTransactionDAO = transferTransactionDAO;  
  
    }  
  
   /**
     * 转账逻辑
     * @param fromAccountId 转账人
     * @param toAccountId  收账人
     * @param amount 金额
     * @return
     * @throws AccountNotExistedException
     * @throws AccountUnderflowException
     */
    public TransferTransaction transfer(String fromAccountId, String toAccountId,  
            BigDecimal amount) throws AccountNotExistedException, AccountUnderflowException {   
    Validate.isTrue(0 < amount.compareTo(BigDecimal.ZERO));  
  
        Account fromAccount = accountDAO.findAccount(fromAccountId);  
        if (null == fromAccount) throw new AccountNotExistedException(fromAccountId);  
        if (0 > fromAccount.getBalance().compareTo(amount)) {  
            throw new AccountUnderflowException(fromAccount, amount);  
        }   
  
        Account toAccount = accountDAO.findAccount(toAccountId);  
        if (null == toAccount) throw new AccountNotExistedException(toAccountId);  
        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));  
        toAccount.setBalance(toAccount.getBalance().add(amount));       
  
        accountDAO.updateAccount(fromAccount);      // 对Hibernate来说这不是必须的  
        accountDAO.updateAccount(toAccount);        // 对Hibernate来说这不是必须的  
        return transferTransactionDAO.create(fromAccountId, toAccountId, amount);  
    }  
}

TransferServiceImpl 类使用了 AccountDAO 和 TranferTransactionDAO,其中 transfer 方法负责整个转帐操作业务。TransferServiceImpl 负责所有的业务逻辑,验证是否超额提取并更新帐户余额。
一切并不复杂,对于这个例子来说,贫血模型工作得非常好!这是因为这个例子相当简单,业务逻辑也不复杂,一旦业务逻辑变得复杂,TransferServiceImpl 就会膨胀。

我有幸见过一个电商项目下单方法写了 3k 行的代码,最后被上司安排了对这部分代码开了分支,花了一个周的时间,针对性的做了一些各模块化方法优化。

总结

简单来说,就是 domain ojbect 包含了不依赖于持久化的领域逻辑,而那些依赖持久化的领域逻辑被分离到 Service 层。

优点:

1、各层单向依赖,结构清楚,易于实现和维护

2、设计简单易行,底层模型非常稳定

缺点:

1、domain object 的部分比较紧密依赖的持久化 domain logic 被分离到 Service 层,显得不够 OO

2、Service 层过于厚重

附言

本篇如有错误,请及时指出,马上修改。

非常非常重要的事情

本文首发于【黑壳博客】,文章持续更新,可以微信搜索【黑壳博客】点个关注 文章第一时间阅读。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...