Active Record 模式、Repository 模式和可测试性(java 中)

发布于 2024-11-01 18:17:29 字数 701 浏览 0 评论 0原文

以下旨在充分利用 Active Record 模式和 Repository 模式的方法有什么缺点(例如在可测试性方面)?

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

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

这种方法真的需要考虑可测试性吗?即使使用纯 Active Record 方法:是否存在数据库逻辑仅代表整个业务逻辑的一小部分的信息系统,并且模拟数据库访问会很有趣?

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

编辑2:也许这篇文章会更好地解释我正在尝试解决的问题:http://moleseyhill.com/blog/2009/07/13/active-record-verses-repository/

What would be the drawback (in terms of testability for example) to the following approach which intends to get the best from Active Record pattern and Repository pattern ?

Each persistent object exposes save() and delete() method, but no static methods to load itself, or to load a list of similar objects : loading from upper layers is done by directly calling a repository, to avoid static methods in persistent objects.

The "save()" and "delete()" methods are only facades, they are delegated to the repository.

Is testability really a concern with this approach ? Even with pure Active Record approach : are there information systems where database logic represents only a small part of the whole business logic, and where it would be interesting to mock the database access ?

EDIT : This approach needs that persistent objects inherit from an AbstractPersistentObject which implements "save()" and "delete()", and it prevents business inheritance, but I read it was better to avoid business inheritance, and to replace it with composition, so it may be an advantage, and not a drawback...?

EDIT2 : Maybe this article will explain better which issues I am trying to solve : http://moleseyhill.com/blog/2009/07/13/active-record-verses-repository/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乖乖兔^ω^ 2024-11-08 18:17:29

有两件事引起了一些担忧。第一个是这句话(强调我的):

[...]是否有信息系统数据库逻辑仅代表整个业务逻辑的一小部分,并且模拟数据库访问会很有趣?

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

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

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

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

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

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

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

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

但是这样的方法意味着调用者已经拥有一个存储库实例,因此他不妨调用直接在存储库上使用 save() 方法。领域模型上的任何持久性方法都将过时。

也许我把你的问题过于简单化了。你想达到什么目的?您只需要 entity.save() 语法,还是想解决更大的问题?

There are two things that raise some concern. The first one is this quote (emphasis mine):

[...] are there information systems where database logic represents only a small part of the whole business logic, and where it would be interesting to mock the database access?

Are you putting business logic in your database? If so: don't do it, it makes it very hard to mock your database. You'd have to duplicate (and maintain!) all of the business logic from the database to your mocks, otherwise your tests are useless.

But how do you know if the mocks correctly implement the business logic? You could write unit tests for your mocks, or reuse the unit tests of your database (you do have them, right?), but that's an approach I'd try to avoid at all costs! Let me repeat: don't ever (have to) write unit tests for your mocks. If you find yourself in such a situation, take a few steps back and review your design, because there's something very wrong.

Putting business logic in the database will only create unnecessary coupling between your model and the database, and immensely complicate your testing layer. Separation of concerns is key here: the model focuses on business logic only and the database focuses on persistence only, nothing else.

Which brings me to my next concern: why do you need save() and delete() methods, which are persistence-related, on your domain model? Persistence doesn't belong in the domain model.

I know, you said that these methods will delegate to a repository, so the domain model (hopefully) doesn't contain the actual persistence logic. But how does it know which repository it should delegate to?

If you are calling a service locater in the save() method, you're making it impossible to save the entity to multiple repositories. You're also hiding the dependency on the repository from the caller, which I consider to be a bad thing.

To solve these issues, you could pass a repository instance to the save() method, like this:

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

But a method like this implies that the caller already has a repository instance, so he might as well call the save() method directly on the repository. Any persistence methods on the domain model would be obsolete.

Perhaps I'm oversimplifying your problem. What is it you are trying to achieve? Do you only want the entity.save() syntax, or are you trying to solve a bigger problem?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文