ddd的战术篇: 如果没有domain object 世界会是怎样的?

前一篇关于domain object的例子。(当然没写完整。)

如果不那么写,会是怎样的效果呢?我觉得我之后列出的代码可能是非常常见的一种写法。而且相比于ddd,大家可能更加熟悉。

贫血模型的世界

首先万年不变的实体(尸体)类
@Setter
@Getter
public class Account {
    private String email;
    private String password;
}

Account对应了可能叫accounts的一张表
然后我们自然而然回去想到CRUD的操作。那当然又是DAO
public interface IAccountDao {
    void insert(Account account);
    void update(Account account);
    Account findById(String email);
}



按照这样的流程,那具体来使用这两个类的肯定就是Service了
public class AccountService {

    @Autowired
    private IAccountDao accountDao;

    public void changePassword(String email, @NonNull String oldPassword, @NonNull String newPassword){
        Account account = accountDao.findById(email);
        if(oldPassword.equals("")){
            throw new IllegalArgumentException("password cannot be empty");
        }
        if(!account.getPassword().equals(encryptPassword(oldPassword))){
            throw new IllegalArgumentException("old password is wrong");
        }
        account.setPassword(encryptPassword(newPassword));

        accountDao.update(account);
    }

    public void createNewAccount(String email, @NonNull String password){
        Account account = new Account();
        account.setPassword(password);
        account.setEmail(email);
        accountDao.insert(account);
    }

    private String encryptPassword(String password){
        // TODO non implmented
        return password;
    }
}



贫血模式的缺点

感觉不是面向对象

这样的写法实体类就是一个数据的载体。业务的逻辑全心全意地交给了service类,实体类对业务一无所知。
感觉类和C的struct也没什么区别啊,就是可以查谁掉了setter方便点?
从宗教问题上讲,面向对象的一个目的就是开放有限接口,隐藏具体实现,具体数据。(当然,有人会说不是啦,面向对象的精髓应该是消息啥的,我们这里不讨论什么才是面向对象啦~)那至少实体类肯定是不符合这个要求的。

数据的完整性容易被破坏

实体类完全开放了setter,等于所有保持数据完整性的责任就交给了service。这是无法用语言特性来保障的,只能教育,加强代码的审核。否则,一不小心谁没通过AccountService,直接new Account()随便设了值就存,数据就可能不正常。

内聚度低

可能会觉得,和domain object的写法相比,类的数量少了啊,内聚度低?有没有搞错?
但是domain object的逻辑,其实就在Account这个entity里(好啦~其实还有很多逻辑在value object。我知道我多写了好多value object造成类数量的增多,自作孽。。。)
第内聚度的另一个后果当然是对外部依赖的增加,如果不太注意减低和控制外部依赖,很可能导致依赖关系的混乱,特别是没有明确分模块模块的情况下。于是A依赖于B,B又依赖于A这种双重依赖一不小心就产生了。或者是一个service类里又注入了十多个service,这些service之间的关系又是乱七八糟。

写自动测试难度高

因为业务逻辑存在于accountService中,所以如果要写测试,测试对象会是accountService。但它有一些成员变量需要注入啊。是的,你可以使用mock啊。
但是,如果按照domain object来写,写自动测试,你不觉得更轻松愉快吗?



贫血模型的使用

也许是我的错觉,我觉得目前来说贫血模式是web应用开发的主流(纯感觉,没有任何可靠根据)。
那贫血模式好在哪里呢?
1 它以最简单的方式,将数据和行为进行了分离(即使它很不面向对象)。
2 相比于充血模型要思考如何合理地建模,充血模型因为业务逻辑都在stateless的servcie类中,所以写起来会比较简单。
个人认为,对于比较简单的系统,或者业务基本能用CRUD来描述的系统,使用贫血模型是个可以考虑的选择。
从经验上来说,当系统变得复杂,贫血模型的系统会更快变得很难维护。(随着系统变得复杂充血模型当然也会遇到困难,但它的逻辑更加分散,所以变得难以维护的速率可能低一点)。
另外,如果使用贫血模型时,做好模块的横向划分,会使它更能对抗复杂的系统。
其实话说回来,domain model,并不是我们不去思考它,它就不存在的。使用贫血模型容易使我们意识不到的domain model变得没有规则,没有规律。就像我们不去考虑类设计,设计出来的类自然就可能混乱。所以如果我们建立好domain model,即使不用贫血模型去实现它,系统的耐复杂度也会变得更好。

你可能感兴趣的:(ddd的战术篇: 如果没有domain object 世界会是怎样的?)