问题描述
关于DDD模型中两个聚合根之间的引用之间的关系,我有几个问题。请参考下面图解的典型客户/订单模型。
例如,如果我想要有关订单客户的详细信息,我需要获取CustomerId并将其传递给ICustomerRepository以获取客户,而不是设置Order对象以直接返回客户吗?我很困惑,因为直接返回客户似乎会使针对模型的代码编写更加容易,并且如果我使用的是NHibernate之类的ORM,则设置起来也不会很困难。但是我相当确定这将违反聚合根目录/存储库之间的界限。
第二,在哪里以及如何对两个聚合根目录执行删除关系的级联?例如,说我希望在删除客户时删除所有关联的订单。 ICustomerRepository.DeleteCustomer()方法应该不引用IOrderRepostiory吗?好像那样会打破聚合/存储库之间的界限?我是否应该有一个CustomerManagment服务来处理删除客户及其关联的同时引用IOrderRepository和ICustomerRepository的订单?在这种情况下,我如何确定人们知道使用服务而不是使用存储库来删除客户。仅仅是为了教育他们如何正确使用模型?
不是真的-尽管有些是出于性能原因会进行更改。
通常,您会模拟关系的1面(例如, Customer.Orders
或 Order.Customer
)进行遍历。另一个可以从适当的存储库中获取(例如, CustomerRepository.GetCustomerFor(Order)
或 OrderRepository.GetOrdersFor(Customer)
)。
OrderRepository $ c $ c>会知道如何使用
ICustomerRepository.FindById(int)
。您可以注入 ICustomerRepository
。有些人可能对此感到不舒服,因此选择将其放入服务层-但我认为这太过分了。存储库之间不存在彼此不了解和使用的特殊原因。
聚合根是允许保留对其他聚合根的引用。实际上,任何东西都可以保留对聚合根的引用。聚合根不能包含对不属于它的非聚合根实体的引用。
例如, Customer
无法保存对 OrderLines
的引用-因为 OrderLines
正确地属于 Order
聚合根。
If (我强调,因为这是一个特殊的要求),实际上是两个在一个用例中,这表明 Customer
应该是您唯一的总根。但是,在大多数实际系统中,我们实际上不会删除与 Order $相关联的
Customer
c $ c> s-我们可能会停用它们,将其 Order
s移至合并的 Customer
等,但是-不必反复删除 Order
s。
话虽这么说,但我不认为这是纯粹的- DDD,大多数人会允许一些宽大的工作,遵循一种工作单元模式,即先删除 Order
s然后删除 Customer
(如果 Order
仍然存在,则失败)。如果愿意,您甚至可以让 CustomerRepository
来完成工作(尽管我希望自己更明确地进行说明)。允许稍后(也可以不)清理孤立的 Order
也是可以接受的。用例在这里起到了很大的作用。
我可能不会为某些事情走一条服务路线,所以与存储库密切相关。至于如何确保使用服务...您只是不要在 CustomerRepository
删除 >。或者,如果删除 Customer
会留下孤立的 Order
s,则抛出错误。
I have a couple questions regarding the relationship between references between two aggregate roots in a DDD model. Refer to the typical Customer/Order model diagrammed below.
First, should references between the actual object implementation of aggregates always be done through ID values and not object references? For example if I want details on the customer of an Order I would need to take the CustomerId and pass it to a ICustomerRepository to get a Customer rather then setting up the Order object to return a Customer directly correct? I'm confused because returning a Customer directly seems like it would make writing code against the model easier, and is not much harder to setup if I am using an ORM like NHibernate. Yet I'm fairly certain this would be violating the boundaries between aggregate roots/repositories.
Second, where and how should a cascade on delete relationship be enforced for two aggregate roots? For example say I want all the associated orders to be deleted when a customer is deleted. The ICustomerRepository.DeleteCustomer() method should not be referencing the IOrderRepostiory should it? That seems like that would be breaking the boundaries between the aggregates/repositories? Should I instead have a CustomerManagment service which handles deleting Customers and their associated Orders which would references both a IOrderRepository and ICustomerRepository? In that case how can I be sure that people know to use the Service and not the repository to delete Customers. Is that just down to educating them on how to use the model correctly?
Not really - though some would make that change for performance reasons.
Generally, you'd model 1 side of the relationship (eg., Customer.Orders
or Order.Customer
) for traversal. The other can be fetched from the appropriate Repository (eg., CustomerRepository.GetCustomerFor(Order)
or OrderRepository.GetOrdersFor(Customer)
).
The OrderRepository
would know how to use an ICustomerRepository.FindById(int)
. You can inject the ICustomerRepository
. Some may be uncomfortable with that, and choose to put it into a service layer - but I think that's overkill. There's no particular reason repositories can't know about and use each other.
Aggregate roots are allowed to hold references to other aggregate roots. In fact, anything is allowed to hold a reference to an aggregate root. An aggregate root cannot hold a reference to a non-aggregate root entity that doesn't belong to it, though.
Eg., Customer
cannot hold a reference to OrderLines
- since OrderLines
properly belongs as an entity on the Order
aggregate root.
If (and I stress if, because it's a peculiar requirement) that's actually a use case, it's an indication that Customer
should be your sole aggregate root. In most real-world systems, however, we wouldn't actually delete a Customer
that has associated Order
s - we may deactivate them, move their Order
s to a merged Customer
, etc. - but not out and out delete the Order
s.
That being said, while I don't think it's pure-DDD, most folks will allow some leniency in following a unit of work pattern where you delete the Order
s and then the Customer
(which would fail if Order
s still existed). You could even have the CustomerRepository
do the work, if you like (though I'd prefer to make it more explicit myself). It's also acceptable to allow the orphaned Order
s to be cleaned up later (or not). The use case makes all the difference here.
I probably wouldn't go a service route for something so intimately tied to the repository. As for how to make sure a service is used...you just don't put a public Delete
on the CustomerRepository
. Or, you throw an error if deleting a Customer
would leave orphaned Order
s.
这篇关于如何在聚合根之间强制执行关系和约束?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!