DDD:在哪里放置持久性逻辑,以及何时使用 ORM 映射
我们正在对我们的 (Java) Web 应用程序模式进行长期、认真的研究。过去,我们遭受了过于贫乏的对象模型以及控制器、服务和 DAO 之间过度程序分离的困扰,简单的值对象(基本上只是数据包)在它们之间传输。我们使用声明性 (XML) 管理的 ORM (Hibernate) 来实现持久性。所有实体管理都在 DAO 中进行。
在尝试转向更丰富的域模型时,我们发现自己在努力解决如何最好地设计持久层的问题。我花了很多时间阅读和思考领域驱动设计模式。不过,我想要一些建议。
首先,我更有信心的事情是:
我们将在前端拥有“瘦”控制器,仅处理 HTTP 和 HTML - 处理表单、验证、UI 逻辑。
我们将有一个无状态业务逻辑服务层,它实现通用算法或逻辑,不知道 UI,但非常了解(并委托给)域模型。
我们将拥有一个更丰富的域模型,其中包含该域模型中对象固有的状态
问题在于坚持。以前,我们的服务将通过 DAO 注入(通过 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 永远从一个层次结构到另一个层次结构。
这里合适的设计选择是什么?
马丁
We are taking a long, hard look at our (Java) web application patterns. In the past, we've suffered from an overly anaemic object model and overly procedural separation between controllers, services and DAOs, with simple value objects (basically just bags of data) travelling between them. We've used declarative (XML) managed ORM (Hibernate) for persistence. All entity management has taken place in DAOs.
In trying to move to a richer domain model, we find ourselves struggling with how best to design the persistence layer. I've spent a lot of time reading and thinking about Domain Driven Design patterns. However, I'd like some advice.
First, the things I'm more confident about:
We'll have "thin" controllers at the front that deal only with HTTP and HTML - processing forms, validation, UI logic.
We'll have a layer of stateless business logic services that implements common algorithms or logic, unaware of the UI, but very much aware of (and delegating to) the domain model.
We'll have a richer domain model which contains state, relationships, and logic inherent to the objects in that domain model.
The question comes around persistence. Previously, our services would be injected (via Spring) with DAOs, and would use DAO methods like find() and save() to perform persistence. However, a richer domain model would seem to imply that objects should know how to save and delete themselves, and perhaps that higher level services should know how to locate (query for) domain objects.
Here, a few questions and uncertainties arise:
Do we want to inject DAOs into domain objects, so that they can do "this.someDao.save(this)" in a save() method? This is a little awkward since domain objects are not singletons, so we'll need factories or post-construction setting of DAOs. When loading entities from a database, this gets messy. I know Spring AOP can be used for this, but I couldn't get it to work (using Play! framework, another line of experimentation) and it seems quite messy and magical.
Do we instead keep DAOs (repositories?) completely separate, on par with stateless business logic services? This can make some sense, but it means that if "save" or "delete" are inherent operations of a domain object, the domain object can't express those.
Do we just dispense with DAOs entirely and use JPA to let entities manage themselves.
Herein lies the next subtlety: It's quite convenient to map entities using JPA. The Play! framework gives us a nice entity base class, too, with operations like save() and delete(). However, this means that our domain model entities are quite closely tied to the database structure, and we are passing objects around with a large amount of persistence logic, perhaps all the way up to the view layer. If nothing else, this will make the domain model less re-usable in other contexts.
If we want to avoid this, then we'd need some kind of mapping DAO - either using simple JDBC (or at least Spring's JdbcTemplate), or using a parallel hierarchy of database entities and "business" entities, with DAOs forever copying information from one hierarchy to another.
What is the appropriate design choice here?
Martin
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的问题和疑虑在这里敲响了有趣的警钟,我认为您对“丰富域模型”的解释有点太过分了。丰富性并不意味着持久化逻辑必须由域对象处理,换句话说,不,它们不应该知道如何保存和删除自己(至少不明确,尽管 Hibernate 实际上添加了一些持久化逻辑)透明地)。这通常被称为持久性无知。
我建议您保留现有的 DAO 注入系统(对于单元测试来说这是一件好事)并保留持久层,同时尝试将一些业务逻辑移动到适合的实体。一个好的起点是识别聚合并建立聚合根。它们通常比其他实体包含更多的业务逻辑。
然而,这并不是说域对象应该包含所有逻辑(尤其是应用程序中许多其他对象所需的逻辑,这些逻辑通常属于服务)。
Your questions and doubts ring an interesting alarm here, I think you went a bit too far in your interpretation of a "rich domain model". Richness doesn't go as far as implying that persistence logic must be handled by the domain objects, in other words, no, they shouldn't know how to save and delete themselves (at least not explicitely, though Hibernate actually adds some persistence logic transparently). This is often referred to as persistence ignorance.
I suggest that you keep the existing DAO injection system (a nice thing to have for unit testing) and leave the persistence layer as is while trying to move some business logic to your entities where it's fit. A good starting point to do that is to identify Aggregates and establish your Aggregate Roots. They'll often contain more business logic than the other entities.
However, this is not to say domain objects should contain all logic (especially not logic needed by many other objects across the application, which often belongs in Services).
我不是 Java 专家,但我在 .NET 代码中使用 NHibernate,因此我的经验应该可以直接转化为 Java 世界。
当使用 ORM(如您提到的 Hibernate)构建域驱动设计应用程序时,好的(我不会说最好)实践之一是在 UI 和域之间创建所谓的应用程序服务。它们与您提到的无状态业务对象类似,但几乎不包含逻辑。它们应该如下所示:
对 DomainObject 包含的对象的任何更改(也将对象添加到集合中)都将由 Hibernate 处理。
您还需要某种 AOP 来拦截应用程序服务方法调用,并在方法执行之前创建 Hibernate 会话,并在方法完成后保存更改(无一例外)。
此处有一个非常好的示例,介绍如何在 Java 中执行 DDD。它基于 Eric Evans 的 'Blue Book' 中的示例问题。应用程序逻辑类示例代码为 此处。
I am not a Java expert, but I use NHibernate in my .NET code so my experience should be directly translatable to the Java world.
When using ORM (like Hibernate you mentioned) to build Domain-Driven Design application, one of good (I won't say best) practices is to create so-called application services between the UI and the Domain. They are similar to stateless business objects you mentioned, but should contain almost no logic. They should look like this:
Any changes to objects contained by DomainObject (also adding objects to collections) will be handled by Hibernate.
You will also need some kind of AOP to intercept application service method invocations and create Hibernate's session before the method executes and save changes after method finishes with no exceptions.
There is a really good sample how to do DDD in Java here. It is based on the sample problem from Eric Evans' 'Blue Book'. The application logic class sample code is here.