DDD:何处放置持久性逻辑,以及何时使用ORM映射

opt*_*ude 13 oop dao design-patterns domain-driven-design

我们正在仔细研究我们的(Java)Web应用程序模式.在过去,我们遭受了过度贫血的对象模型以及控制器,服务和DAO之间的过度程序分离,其中简单的价值对象(基本上只是数据包)在它们之间传播.我们使用声明性(XML)管理的ORM(Hibernate)来实现持久性.所有实体管理都在DAO中进行.

在尝试转向更丰富的域模型时,我们发现自己在如何最好地设计持久层方面遇到了困难.我花了很多时间阅读和思考领域驱动设计模式.但是,我想要一些建议.

首先,我更自信的事情:

  • 我们前面有"瘦"控制器,只处理HTTP和HTML - 处理表单,验证,UI逻辑.

  • 我们将有一层无状态业务逻辑服务,它们实现常见的算法或逻辑,不知道UI,但非常了解(并委托)域模型.

  • 我们将拥有一个更丰富的域模型,其中包含该域模型中对象固有的状态,关系和逻辑.

问题出在持久性上.以前,我们的服务将通过Spring注入(通过Spring),并使用find()和save()等DAO方法来执行持久性.但是,更丰富的域模型似乎暗示对象应该知道如何保存和删除自己,也许更高级别的服务应该知道如何定位(查询)域对象.

在这里,出现了一些问题和不确定性:

  • 我们是否要将DAO注入域对象,以便它们可以在save()方法中执行"this.someDao.save(this)"?这有点尴尬,因为域对象不是单例,所以我们需要工厂或DAO的后期构建设置.从数据库加载实体时,这会变得混乱.我知道Spring AOP可以用于此,但我无法使用它(使用Play!框架,另一行实验),它看起来非常混乱和神奇.

  • 我们是否将DAO(存储库?)完全分开,与无状态业务逻辑服务相提并论?这可能有一定道理,但这意味着如果"保存"或"删除"是域对象的固有操作,则域对象无法表达这些操作.

  • 我们是否完全免除了DAO并使用JPA让实体自行管理.

下面是下一个细微之处:使用JPA映射实体非常方便.表演!框架为我们提供了一个很好的实体基类,具有save()和delete()等操作.但是,这意味着我们的域模型实体与数据库结构紧密相关,并且我们使用大量持久性逻辑传递对象,可能一直到视图层.如果不出意外,这将使域模型在其他环境中的可重用性降低.

如果我们想避免这种情况,那么我们需要某种映射DAO - 使用简单的JDBC(或至少是Spring的JdbcTemplate),或使用数据库实体和"业务"实体的并行层次结构,DAO永远复制来自一个层次结构到另一个

这里适当的设计选择是什么?

马丁

Szy*_*ega 2

我不是 Java 专家,但我在 .NET 代码中使用 NHibernate,因此我的经验应该可以直接转化为 Java 世界。

当使用 ORM(如您提到的 Hibernate)构建领域驱动设计应用程序时,好的(我不会说最好)实践之一是在 UI 和域之间创建所谓的应用程序服务。它们与您提到的无状态业务对象类似,但几乎不包含逻辑。它们应该看起来像这样:

public void SayHello(int id, String helloString)
{
    SomeDomainObject target = domainObjectRepository.findById(id); //This uses Hibernate to load the object.

    target.sayHello(helloString); //There is a single domain object method invocation per application service method.

    domainObjectRepository.Save(target); //This one is optional. Hibernate should already know that this object needs saving because it tracks changes.
}
Run Code Online (Sandbox Code Playgroud)

对 DomainObject 包含的对象的任何更改(也将对象添加到集合中)都将由 Hibernate 处理。

您还需要某种 AOP 来拦截应用程序服务方法调用,并在方法执行之前创建 Hibernate 会话,并在方法完成后保存更改(无一例外)。

这里有一个非常好的示例,说明如何在 Java 中执行 DDD 。它基于 Eric Evans 的“蓝皮书”中的示例问题。应用程序逻辑类示例代码在这里