领域模型应该使用事件保持自身的一致性吗?
我正在开发一个尝试使用域模型的应用程序。这个想法是将业务逻辑保留在域模型中的对象内。现在,很多事情都是通过对象订阅相关对象来对其中的变化做出反应来完成的。这是通过 PropertyChanged 和 CollectionChanged 完成的。除以下情况外,此操作正常:
复杂操作:应将大量更改作为一个组进行处理(而不是单个属性/集合更改)。我应该/如何“建立”交易?
持久性:我使用 NHibernate 来实现持久性,这也使用了类的公共属性设置器。当 NHibernate 访问该属性时,会完成大量业务逻辑(这似乎没有必要)。我应该为 NHibernate 使用自定义 setter 吗?
总的来说,将所有逻辑推入域模型似乎会使域模型变得相当复杂。有什么想法吗???
这是一个“示例”问题(抱歉我使用的蹩脚工具):
您可以查看我的项目容器及其下面的对象通过订阅相互反应。现在对网络的更改是通过 NetworkEditor 完成的,但该编辑器不了解 NetworkData。有时,这些数据甚至可能在另一个程序集中定义。该流程从用户->网络编辑器->网络->网络数据以及所有其他感兴趣的对象开始。这似乎没有规模。
I am working on an application where we try to use a Domain Model. The idea is to keep the business logic inside the objects in the Domain Model. Now a lot is done by objects subscribing to related objects to react to changes in them. This is done through PropertyChanged and CollectionChanged. This work OK except in the following:
Complex actions : Where a lot of changes should be handled as a group (and not individual property/collection changes). Should I / how can I 'build' transactions?
Persistency : I use NHibernate for persistency and this also uses the public property setters of classes. When NHibernate hits the property a lot of bussiness logic is done (which seems unnecessary). Should I use custom setters for NHibernate?
Overal it seems that pushing all logic in the domain model makes the domain model rather complex. Any ideas???
Here's a 'sample' problem (sorry for the crappy tooling i use):
You can see the Project my container and objects below it are reacting to each other by subscribing. Now changes to Network are done via NetworkEditor but this editor has no knowledge of NetworkData. This data might even be defined in a another assembly sometimes. The flow goes from user->NetworkEditor->Network->NetworkData and the all other object interested. This does not seem to scale.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我担心 DDD 和 PropertyChanged/CollactionChanged 事件的组合现在可能是最好的主意。问题是,如果你的逻辑基于这些事件,那么管理复杂性就非常困难,因为一个 PropertyChanged 会导致另一个 PropertyChanged ,很快你就会失去控制。
ProportyChanged 事件和 DDD 不完全契合的另一个原因是,在 DDD 中,每个业务操作都应该尽可能明确。请记住,DDD 应该将技术内容带入商业世界,而不是相反。基于 PropertyChanged/CollectionChanged 似乎不是很明确。
在 DDD 中,主要目标是保持聚合内部的一致性,换句话说,您需要以这样的方式对聚合进行建模,即您调用聚合的任何操作都是有效且一致的(当然,如果操作成功的话)。
如果您正确构建模型,则无需担心“构建”事务 - 聚合操作本身应该是事务。
我不知道您的模型是什么样子,但您可能会考虑将聚合树中的职责“向上”移动一级,很可能在流程中添加其他逻辑实体,而不是依赖 PropertyChanged 事件。
示例:
假设您有一组具有状态的付款,并且每当付款发生变化时,您想要重新计算客户订单的总余额。您可以执行以下操作,而不是订阅付款收款的更改并在收款更改时调用客户的方法:
您可能已经注意到,我们重新计算单个订单的余额,而不是整个客户的余额 - 在大多数情况下没关系,因为客户属于另一个聚合,并且可以在需要时简单地查询其余额。这正是显示这种“仅在聚合内的一致性”的部分 - 我们此时不关心任何其他聚合,我们只处理单个订单。如果这不能满足要求,则域建模不正确。
我的观点是,在 DDD 中,没有适合每种场景的单一良好模型 - 您必须了解业务如何运作才能取得成功。
如果您看一下上面的示例,您会发现无需“构建”交易 - 整个交易位于
SetPaymentAsReceived
方法中。在大多数情况下,一个用户操作应该导致具有聚合的实体上的一种特定方法 - 该方法明确与业务操作相关(当然该方法可能会调用其他方法)。对于 DDD 中的事件,有一个领域事件的概念,但这些事件与 PropertyChanged/CollectionChanged 技术事件没有直接关系。领域事件表示聚合已完成的业务操作(事务)。
当然,它应该用于业务逻辑复杂的场景。然而,如果领域建模正确,那么就很容易管理和控制这种复杂性,这就是 DDD 的优势之一。
提供示例后添加:
好的,创建一个名为 Project 的聚合根怎么样 - 当您从存储库构建聚合根时,您可以用 NetworkData 填充它,操作可能如下所示:
NetworkEditor 不会直接在 Network 上操作,而是通过 Project使用网络 ID。
I fear that combination of DDD and PropertyChanged/CollactionChanged events might now be the best idea. The problem is, that if you base your logic around these events it is extremely hard to manage the complexity as one PropertyChanged leads to another and another and soon enough you loose control.
Another reason why ProportyChanged events and DDD doesn't exactly fit is that in DDD every business operation should be as explicit as possible. Keep in mind that DDD is supposed to bring technical stuff into the world of business, not the other way around. And basing on the PropertyChanged/CollectionChanged doesn't seem very explicit.
In DDD the main goal is to keep consistency inside aggregate, in other words, you need to model the aggregate in such way, that whatever operation you invoke the aggregate is valid and consistent (if the operation succeeds of course).
If you build your model right that there's no need to worry about 'building' transaction - an operation on aggregate should be a transaction itself.
I don't know how your model looks like, but you might consider moving the responsibilities one level 'up' in the aggregate tree, quite possibly adding additional logical entities in the process, instead of relying on the PropertyChanged events.
Example:
Lets assume you have a collection of payments with statuses and whenever a payment changes, you want to recalculate the total balance of customer orders. Instead of subscribing changes to the payments collection and calling a method on customer when collection changes, you might do something like this:
You might have noticed, that we recalculate the balance of single order and not the balance of entire customer - and in most cases that's ok as customer belongs to another aggregate and its balance can be simply queried when needed. That is exactly the part that shows this 'consistency only within aggregate' thingy - we don't care about any other aggregate at this point, we only deal with single order. If that's not ok for requirements, then the domain is modeled incorrectly.
My point is, that in DDD there's no single good model for every scenario - you have to understand how the business works to be successful.
If you take a look at the example above, you'll see that there is no need to 'build' the transaction - entire transaction is located in
SetPaymentAsReceived
method. In most cases, one user action should lead to one particular method on an entity withing aggregate - this method explicitly relates to business operation (of course this method may call other methods).As for events in DDD, there is a concept of Domain Events, however these are not directly related with PropertyChanged/CollectionChanged technical events. Domain Events indicate the business operations (transactions) that have been completed by an aggregate.
Of course it does as it is supposed to be used for scenarios with complex business logic. However if the domain is modeled correctly then it is easy to manage and control this complexity and that's one of the advantages of DDD.
Added after providing example:
Ok, and what about creating an aggregate root called Project - when you build aggregate root from Repository, you can fill it with NetworkData and the operation might look like this:
NetworkEditor would not operate on Network directly, rather through Project using NetworkId.