Active Record模式,存储库模式和可测试性(在java中)

时间:2011-04-15 13:29:07

标签: design-patterns activerecord domain-driven-design repository-pattern ddd-repositories

以下方法有哪些缺点(例如在可测试性方面),这些方法旨在从 Active Record 模式和存储库模式中获得最佳效果?

每个持久对象都公开save()和delete()方法,但没有静态方法来加载自身,或者加载类似对象的列表:从上层加载是通过直接调用存储库来完成的,以避免静态方法持久对象。

“save()”和“delete()”方法只是外观,它们被委托给存储库。

这种方法真的关注可测试性吗?即使采用纯Active Record方法:是否存在信息系统,其中数据库逻辑仅代表整个业务逻辑的一小部分,以及模拟数据库访问的有趣之处?

编辑:这种方法需要持久对象从实现“save()”和“delete()”的AbstractPersistentObject继承,并且它会阻止业务继承,但我认为最好避免业务继承,并用组合替换它,所以它可能是一个优点,而不是缺点......?

EDIT2 :也许这篇文章会更好地解释我要解决的问题:http://moleseyhill.com/blog/2009/07/13/active-record-verses-repository/

1 个答案:

答案 0 :(得分:6)

有两件事引起了一些关注。第一个是这个引用(强调我的):

  

[...]是否存在信息系统,其中数据库逻辑仅代表整个业务逻辑的一小部分,以及模拟数据库访问会感兴趣的地方?

您是否将业务逻辑放在数据库中?如果是这样:不要这样做,这使得非常难以模拟您的数据库。您必须将所有业务逻辑从数据库复制(并维护!)到您的模拟,否则您的测试将毫无用处。

但是,您如何知道模拟是否正确实现了业务逻辑?您可以为您的模拟编写单元测试,或重用数据库的单元测试(您确实拥有它们,对吗?),但这是我不惜一切代价避免的方法!让我再说一遍:不要(必须)为你的模拟编写单元测试。如果您发现自己处于这种情况,请退后几步并检查您的设计,因为这是非常错误的。

将业务逻辑放在数据库中只会在模型和数据库之间产生不必要的耦合,并使测试层变得非常复杂。关注点分离是关键:模型仅关注业务逻辑,数据库仅关注持久性,而不关注其他任何内容。

这让我想到了下一个问题:为什么在您的域模型中需要save()delete()方法,这些方法与持久性相关? 持久性不属于域模型。

我知道,你说过这些方法会委托给一个存储库,所以域模型(希望如此)不包含实际的持久性逻辑。但是它如何知道应该委托哪个存储库?

如果您在save()方法中调用服务定位器,则无法将实体保存到多个存储库。你也是从调用者那里隐藏了对存储库的依赖,我认为这是件坏事。

要解决这些问题,您可以将存储库实例传递给save()方法,如下所示:

public class Foo extends AbstractPersistentObject {
    public void saveTo(IFooRepository repository) {
        repository.save(this);
    }
}

但是这样的方法意味着调用者已经有了一个存储库实例,所以他也可以直接在存储库上调用save()方法。域模型上的任何持久性方法都将过时。

也许我过分简化了你的问题。你想要实现的目标是什么?您是否只想要entity.save()语法,或者您是否想要解决更大的问题?