坚持无知和DDD现实

时间:2013-08-21 06:50:15

标签: persistence entity domain-driven-design persistence-ignorance

我试图用很少的努力实现完全有效的持久性无知。我有很多问题:

最简单的选项

这真的很简单 - 是否可以像在SOA中那样使用Spring Data注释注释实体(但是让它们真正做逻辑)?除了必须在实体中使用持久性注释之外还有什么后果,这实际上并不遵循PI原则?我的意思是Spring Data的情况确实如此 - 它提供了很好的存储库,可以完成DDD中的存储库应该做的事情。问题在于实体本身呢......

更难选择

为了让实体不知道它所运行的数据来自哪里,通过构造函数将这些数据作为接口注入是很自然的。另一个优点是我们总是可以执行延迟加载 - 例如我们默认在Neo4j图数据库中。缺点是Aggregates(由Entities组成)将完全了解所有数据,即使他们不使用它们 - 可能会导致调试困难,因为数据完全暴露(DAO将像Aggregates一样分层)。这也会迫使我们为存储库使用一些适配器,因为它们不再存储真实的实体......并且任何翻译都是丑陋的...另一件事是我们不能在没有这样的DAO的情况下实例化实体 - 尽管可能存在 - 域中的内存实现......再次,更多层。有人说注入DAO确实会破坏PI。

最难的选择

实体可以围绕一个懒惰的加载器,它决定数据应该来自何处。它既可以是内存中也可以是数据库内,它可以处理任何需要事务的操作等等。虽然复杂的层,但可能在一定程度上是通用的......?阅读一下here

你知道其他任何解决方案吗?或者也许我错过了所提到的东西。请分享您的想法!

4 个答案:

答案 0 :(得分:12)

我免费实现持久性无知(几乎),这是正确域建模的副作用。

特别是:

  • 如果你正确定义了每个上下文的边界,你将获得不需要延迟加载的小实体(实际上它成为DDD项目中的反模式/代码气味)
  • 如果您不能简单地将SQL用于您的存储库,请将一组DTO映射到您的数据库模式,并将它们用于工厂以初始化实体类。

在DDD项目中,持久性无知与域模型本身相关,而不是与存储库,工厂和其他应用程序代码相关。实际上,您将来不太可能更改ORM和/或数据库。

持久性忽略域模型背后的唯一(但非常强大)理性是关注点分离:在域模型中,您应该表达业务不变量!坚持是一种基础设施问题!

例如,如果没有持久性无知(以及延迟加载),域模型应该处理来自db的可能异常,它的复杂性会增加,业务规则也会隐藏在技术细节之下。

答案 1 :(得分:7)

我个人认为在尝试使用与ORM相同的实体时,几乎不可能实现干净的域模型。

我的解决方案是按照我认为合适的方式对我的域实体进行建模,并确保任何ORM实体不会泄漏到存储库之外。这意味着我的存储库接受并返回域实体。

这意味着您失去了“大多数ORM善良”并最终“使用您的ORM进行简单的CRUD操作”。

这两种权衡对我来说都很好,我宁愿拥有一个我可以使用的干净的域模型,而不是一个被我的DB或ORM中的人工制品污染的模型。它还减少了我“与我的ORM搏斗”花费的时间为零。

作为旁注,我发现文档数据库更适合DDD。

答案 2 :(得分:1)

在您的域模型中提供持久性映射后:

  • 您的代码取决于框架。如果您决定更改此框架,则需要更改持久层和模型层源代码 - 更多工作,更多更改,更多代码合并等。
  • 您的域模型jar文件取决于spring / nhibernate jar等。
  • 您的类越来越大,业务代码和持久性相关代码的增长方式

我承认我不理解更难,更难选项。

我们为域实体使用了分离的接口和实现。使用Hibernate和存储库提供单独的映射文件。

使用工厂(或以后的存储库)创建实体,在持久层中生成标识符,实体在持久化之前不需要它。

延迟加载由List的特殊实现提供一次:

  1. 实体的映射包含它
  2. 实体/聚合从持久层获取
  3. 唯一的问题与事务有关,因为当您在事务范围之外使用延迟加载的集合时,它会失败。

答案 3 :(得分:1)

除非我碰到一堵石墙,否则我会遵循最简单的选择。当你采用pi原则时,也存在诸如this之类的陷阱。

有些妥协是可以接受的。

public class Order {
    private String status;//my orm does not support enum

    public Status status() {
        return Status.of(this.status);
    }

    public is(Status status) {
        return status() == status;//use status() instead of getStatus() in domain model
    }
}