通过可达性实现 ORM 持久性违反了聚合根边界?
最常见的 ORM 通过可达性来实现持久性,无论是作为默认的对象图更改跟踪机制还是可选的。
通过可达性实现持久性意味着 ORM 将检查聚合根对象图,并确定是否有任何未存储在其标识映射 (Linq2Sql) 内或没有标识列集 (NHibernate) 的对象(也间接)可达。
在 NHibernate 中,这对应于 cascade="save-update"
,对于 Linq2Sql,它是唯一受支持的机制。它们两者都做,但是仅针对事物的“添加”方面实现它,从聚合根图中删除的对象必须被显式标记为删除。
在 DDD 上下文中,每个聚合根都使用一个存储库。聚合根内的对象只能保存对其他聚合根的引用。由于可访问性的持久性,尽管根本没有调用相应的存储库,但另一个根可能会被插入到数据库事件中!
考虑以下两个聚合根:Contract
和 Order
。 Request
是合约聚合的一部分。 对象图看起来像Contract->Request->Order
。每次承包商提出请求时,都会创建相应的订单。由于这涉及到两个不同的Aggregate Root,所以这个操作被封装在一个Service中。
//Unit Of Work begins
Request r = ...;
Contract c = ContractRepository.FindSingleByKey(1);
Order o = OrderForRequest(r); // creates a new order aggregate
r.Order = o; // associates the aggregates
c.Request.Add(r);
ContractRepository.SaveOrUpdate(c);
// OrderAggregate is reachable and will be inserted
由于此操作发生在服务中,我仍然可以手动调用 OrderRepository,但我不会被迫这样做!通过可达性进行持久化是聚合根中的一个非常有用的功能,但是我认为没有办法强制执行我的聚合边界。
我在这里忽略了什么吗?遇到这样的情况你会如何处理?
编辑:在 NHibernate 中,确实可以通过不使用 cascade="save-update"
标记聚合根关联来强制执行聚合根边界。然而我还是坚持使用 Linq2Sql。
Most common ORMs implement persistence by reachability, either as the default object graph change tracking mechanism or an optional.
Persistence by reachability means the ORM will check the aggregate roots object graph and determines wether any objects are (also indirectly) reachable that are not stored inside it's identity map (Linq2Sql) or don't have their identity column set (NHibernate).
In NHibernate this corresponds to cascade="save-update"
, for Linq2Sql it is the only supported mechanism. They do both, however only implement it for the "add" side of things, objects removed from the aggregate roots graph must be marked for deletion explicitly.
In a DDD context one would use a Repository per Aggregate Root. Objects inside an Aggregate Root may only hold references to other Aggregate Roots. Due to persistence by reachability it is possible this other root will be inserted in the database event though it's corresponding repository wasn't invoked at all!
Consider the following two Aggregate Roots: Contract
and Order
. Request
is part of the Contract Aggregate.
The object graph looks like Contract->Request->Order
. Each time a Contractor makes a request, a corresponding order is created. As this involves two different Aggregate Roots, this operation is encapsulated by a Service.
//Unit Of Work begins
Request r = ...;
Contract c = ContractRepository.FindSingleByKey(1);
Order o = OrderForRequest(r); // creates a new order aggregate
r.Order = o; // associates the aggregates
c.Request.Add(r);
ContractRepository.SaveOrUpdate(c);
// OrderAggregate is reachable and will be inserted
Since this Operation happens in a Service, I could still invoke the OrderRepository manually, however I wouldn't be forced to!. Persistence by reachability is a very useful feature inside Aggregate Roots, however I see no way to enforce my Aggregate Boundaries.
Am I overlooking something here? How would you deal with such a scenario?
EDIT: In NHibernate it would indeed be possible to enforce the aggregate root boundary by not marking the aggregate root association with cascade="save-update"
. I'm stuck with Linq2Sql however.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
老实说,通过可达性实现持久性并不是聚合根边界的真正问题。请记住,聚合根可以很好地相互引用。通常,我会使用一个聚合根来创建另一个聚合根(例如
Customer.CreateOrder
)。 Order 和 Customer 一样都是根,我仍然可以执行Customer.Orders[0].Comments = "Foo"
操作。我不习惯更改域模型,也不保留更改,而是让它们消失。在我看来,这不是一个现实的用例。
Honestly, persistence by reachability isn't really an issue with aggregate root boundaries. Remember, aggregate roots can reference each other just fine. Often, I'll use one aggregate root to create another (
Customer.CreateOrder
for example). The Order is a root as is Customer, and I can still doCustomer.Orders[0].Comments = "Foo"
.I'm not in the habit of changing the domain model and not persisting changes, but letting them evaporate. It's just not a realistic use case IMO.