【问题标题】:Domain driven design and transactions in Spring environmentSpring 环境中的领域驱动设计和事务
【发布时间】:2011-01-06 16:45:28
【问题描述】:

我曾经围绕贫乏的领域模型设计我的应用程序,所以我有许多存储库对象,它们被注入到大而胖的事务感知服务层。这种模式称为事务脚本。这不被认为是一种好的做法,因为它会导致程序代码,所以我想继续进行领域驱动设计。

在阅读了网络上的几篇文章、听了 Chris Richardson 关于 Parleys 的演讲并阅读了 POJOs in Action 的 DDD 章节之后,我想我了解了全局。

问题是,我不知道如何在我的应用程序中组织事务。 Chis Richardson 在他的书中指出:

表示层通过调用来处理来自用户浏览器的 HTTP 请求 直接或间接通过外观的领域模型,正如我 上一章中描述的是 POJO 或 EJB。

到目前为止还不错,但 InfoQ article 上的 Srini Penchikala 表示:

一些开发人员更喜欢在 DAO 类中管理事务,这是一种糟糕的设计。这会导致事务控制过于细化,无法灵活管理事务跨越多个域对象的用例。服务类应该处理事务;这样,即使事务跨越多个域对象,服务类也可以管理事务,因为在大多数用例中,服务类处理控制流。

好的,所以如果我理解正确的话,存储库类不应该是事务性的,服务层(现在更薄了)是事务性的(就像以前在事务脚本模式中一样)。但是如果领域对象被表示层直接调用呢?这是否意味着我的域对象应该具有事务行为?以及如何在Spring或EJB环境中实现?

这对我来说似乎有点奇怪,所以如果有人能澄清这一点,我会很高兴。谢谢。

【问题讨论】:

  • 我添加了 java 标签,因为它涉及各种 DI+ORM(甚至不仅在 java 中,而且这是你的上下文)

标签: java design-patterns spring oop domain-driven-design


【解决方案1】:

this extremely useful blog-post。它解释了如何在不失去 Spring 和 JPA 功能的情况下实现平滑的 DDD。它以@Configurable 注释为中心。

我对这些问题的看法有点不受欢迎。贫血的数据模型其实并没有错。您有两个对象,而不是一个带有数据+操作的对象——一个带有数据,一个带有操作。您可以将它们视为一个对象 - 即满足 DDD,但为了更易于使用,它们在物理上是分开的。。从逻辑上讲,它们是相同的。

是的,这会破坏封装,但它不会让您使用一些“魔法”(aop + java 代理)来实现您的目标。

至于交易 - 有一种叫做交易传播的东西。 Spring 通过@Transactional(propagation=Propagation.REQUIRED) 支持它。 See this, point 9.5.7。如果您希望您的事务跨越多个方法(多个对象),您可以相应地更改传播属性。

您也可以在适当的情况下在您的服务层中使用@Transactional,但是当您想要使用简单的单步操作(例如“保存”)时,这可能会引入很多样板服务类。

【讨论】:

  • 您好,谢谢您的回答。我已经读过那篇文章了。前两个提议的解决方案是不可接受的。我也不喜欢使用休眠拦截器的解决方案,因为(据我所知)它只解决了休眠实例化 bean 的注入。 AspectJ 解决方案似乎相当不错。我只需要弄清楚如何在 @Configurable 类中使用事务。但它没有回答这个问题,我的域模型是否应该是事务性的。如果没有,交易应该在哪里。
  • 我添加了一段关于交易的内容。
  • 我知道,但您发布的文章提到,@Configurable 类不适用于 @Transactional。
  • 不完全——本文下方的评论olafsblog.sysbsb.de/… 表明这是可能的。
  • 哦,我明白了,谢谢。因此,您建议我应该将存储库对象注入实体并使用@Configurable 和@Transactional 使实体具有事务意识。我其实很喜欢这个想法,但不知道它是否被认为是一种好的做法。
【解决方案2】:

到目前为止,我个人认为在 Spring 和 Hibernate 中应用 DDD 是拥有一个无状态事务服务层并通过它访问域对象。所以我这样做的方式是域模型根本不知道事务,这完全由服务处理。

有一个example application 可能会对您有所帮助。看起来 Eric Evans 参与了创作。

【讨论】:

  • 感谢您的回答。因此,例如,当您想要持久化一个实体时,您可以调用 service.save(entity)。我的目标是通过调用 entity.save() 来保存实体,正如 Craig Wells 所述:groups.google.ca/group/EtoE/browse_thread/thread/…
  • 你说得对,我的代码执行 service.save(entity) 之类的操作。我不喜欢 'entity.save()' 方法,我会阅读你链接到的文章,并用解释编辑我的答案。)
  • 如果是 service.save(entity),域对象中还保留什么逻辑?
  • 嗯,这是一个贫乏的领域模型,领域对象只是数据持有者,不包含业务逻辑。
  • 不,如果您查看我链接到的 DDD 示例,域对象中有大量业务逻辑。我不同意在域对象上没有保存方法足以证明它是贫血的。
【解决方案3】:

我认为开始使用 DDD 和 Spring 并拥有如何处理事务的良好示例的一种简单方法是查看 Spring Roo 附带的示例应用程序之一。 Roo 编写了遵循 DDD 原则的代码。它严重依赖于 AspectJ。我怀疑它是在 SpringSource 的重量级人物之一 Ramnivas Laddad in this talk 提出的想法(早在 2006 年)的基础上实施的。

【讨论】:

    【解决方案4】:

    很遗憾,我没有让自己熟悉 Spring,但我会就您对 DDD 的理解提供反馈。阅读您对事务的描述,很明显您不了解 DDD,尤其是聚合、聚合根和存储库的概念。

    在 DDD 中事务不能跨越聚合,因此一个聚合将始终有一个事务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多