且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在聚合根之间强制执行关系和约束?

更新时间:2023-12-01 14:43:16


首先,应该聚合之间的引用始终是通过ID值完成的,而不是实际对象的引用?


不是真的-尽管有些是出于性能原因会进行更改。


例如,如果我要获取订单客户的详细信息,则需要获取CustomerId并将其传递给ICustomerRepository以获取客户,而不是设置Order对象以直接返回客户?


通常,您会模拟关系的1面(例如, Customer.Orders Order.Customer )进行遍历。另一个可以从适当的存储库中获取(例如, CustomerRepository.GetCustomerFor(Order) OrderRepository.GetOrdersFor(Customer))。


这不是说OrderRepository必须了解有关如何创建客户的知识吗?


OrderRepository $ c $ c>会知道如何使用 ICustomerRepository.FindById(int)。您可以注入 ICustomerRepository 。有些人可能对此感到不舒服,因此选择将其放入服务层-但我认为这太过分了。存储库之间不存在彼此不了解和使用的特殊原因。


我很困惑,因为直接返回客户似乎会使我更容易针对模型编写代码,并且如果我使用的是NHibernate之类的ORM,设置起来也不会很困难。但是我相当确定这将违反聚合根/存储库之间的边界。


聚合根允许保留对其他聚合根的引用。实际上,任何东西都可以保留对聚合根的引用。聚合根不能包含对不属于它的非聚合根实体的引用。



例如, Customer 无法保存对 OrderLines 的引用-因为 OrderLines 正确地属于 Order 聚合根。


第二,在何处以及如何执行级联删除关系


If (我强调,因为这是一个特殊的要求),实际上是两个在一个用例中,这表明 Customer 应该是您唯一的总根。但是,在大多数实际系统中,我们实际上不会删除 Order $相关联的 Customer c $ c> s-我们可能会停用它们,将其 Order s移至合并的 Customer 等,但是-不必反复删除 Order s。



话虽这么说,但我不认为这是纯粹的- DDD,大多数人会允许一些宽大的工作,遵循一种工作单元模式,即先删除 Order s然后删除 Customer (如果 Order 仍然存在,则失败)。如果愿意,您甚至可以让 CustomerRepository 来完成工作(尽管我希望自己更明确地进行说明)。允许稍后(也可以不)清理孤立的 Order 也是可以接受的。用例在这里起到了很大的作用。


我应该改为使用CustomerManagment服务来处理删除Customer及其关联的Order的情况,该服务将引用这两个IOrderRepository和ICustomerRepository?在这种情况下,我如何确定人们知道使用服务而不是使用存储库来删除客户。仅仅是为了教育他们如何正确使用模型?


我可能不会为某些事情走一条服务路线,所以与存储库密切相关。至于如何确保使用服务...您只是不要在 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?

First, should references between aggregates always be done through ID values and not actual object references?

Not really - though some would make that change for performance reasons.

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?

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)).

Wouldn't that mean that the OrderRepository would have to know something about how to create a Customer? Wouldn't that be beyond what OrderRepository should be responsible for...

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.

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.

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.

Second, where and how should a cascade on delete relationship be enforced for two aggregate roots?

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 Orders - we may deactivate them, move their Orders to a merged Customer, etc. - but not out and out delete the Orders.

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 Orders and then the Customer (which would fail if Orders 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 Orders to be cleaned up later (or not). The use case makes all the difference here.

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?

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 Orders.