1

我正在开发一个我们尝试使用域模型的应用程序。这个想法是将业务逻辑保留在域模型中的对象内。现在,订阅相关对象以对它们的变化做出反应的对象完成了很多工作。这是通过 PropertyChanged 和 CollectionChanged 完成的。这项工作正常,但以下情况除外:

复杂操作:应将大量更改作为一个组处理(而不是单个属性/集合更改)。我应该/如何“建立”交易?

持久性:我使用 NHibernate 来实现持久性,这也使用了类的公共属性设置器。当 NHibernate 命中该属性时,会完成很多业务逻辑(这似乎没有必要)。我应该为 NHibernate 使用自定义设置器吗?

总体而言,在域模型中推动所有逻辑似乎使域模型相当复杂。有任何想法吗???

这是一个“示例”问题(对不起,我使用了糟糕的工具):

在此处输入图像描述

您可以看到项目我的容器和它下面的对象通过订阅相互反应。现在对 Network 的更改是通过 NetworkEditor 完成的,但是这个编辑器不知道 NetworkData。有时甚至可能在另一个程序集中定义此数据。流程来自 user->NetworkEditor->Network->NetworkData 和所有其他感兴趣的对象。这似乎没有规模。

4

1 回答 1

2

我担心 DDD 和 PropertyChanged/ColactionChanged 事件的组合现在可能是最好的主意。问题是,如果您将逻辑基于这些事件,则很难管理复杂性,因为一个 PropertyChanged 会导致另一个 PropertyChanged 导致另一个又一个,并且很快您就会失去控制。

ProportyChanged 事件和 DDD 不完全匹配的另一个原因是,在 DDD 中,每个业务操作都应该尽可能明确。请记住,DDD 应该将技术内容带入商业世界,而不是相反。并且基于 PropertyChanged/CollectionChanged 似乎不是很明确。

在 DDD 中,主要目标是保持聚合内部的一致性,换句话说,您需要以这样的方式对聚合进行建模,即您调用聚合的任何操作都是有效且一致的(当然,如果操作成功的话)。

如果您正确构建模型,则无需担心“构建”事务 - 聚合操作本身应该是事务。

我不知道您的模型是什么样子,但您可能会考虑将责任在聚合树中“向上”移动一级,很可能在流程中添加其他逻辑实体,而不是依赖 PropertyChanged 事件。

例子:

假设您有一组带有状态的付款,并且每当付款发生变化时,您都想重新计算客户订单的总余额。您可以执行以下操作,而不是订阅付款集合的更改并在集合更改时调用客户的方法:

    public class CustomerOrder
    {
        public List<Payment> Payments { get; }
        public Balance BalanceForOrder { get; }

        public void SetPaymentAsReceived(Guid paymentId)
        {
            Payments.First(p => p.PaymentId == paymentId).Status = PaymentStatus.Received;
            RecalculateBalance();
        }
    }

您可能已经注意到,我们重新计算了单个订单的余额,而不是整个客户的余额 - 在大多数情况下,这是可以的,因为客户属于另一个聚合,并且可以在需要时简单地查询其余额。这正是显示这种“仅在聚合内的一致性”的部分——我们此时不关心任何其他聚合,我们只处理单个订单。如果这对需求不合适,那么域的建模不正确。

我的观点是,在 DDD 中,没有适用于每个场景的单一好模型——您必须了解业务如何运作才能获得成功。

如果您看一下上面的示例,您会发现没有必要“构建”事务 - 整个事务位于SetPaymentAsReceived方法中。在大多数情况下,一个用户操作应该导致实体上的一个特定方法具有聚合 - 此方法显式与业务操作相关(当然此方法可能会调用其他方法)。

至于 DDD 中的事件,有一个领域事件的概念,但这些与 PropertyChanged/CollectionChanged 技术事件没有直接关系。领域事件表示聚合完成的业务操作(事务)。

总体而言,在域模型中推动所有逻辑似乎使域模型相当复杂

当然,它应该用于具有复杂业务逻辑的场景。但是,如果对域进行了正确建模,那么很容易管理和控制这种复杂性,这就是 DDD 的优势之一。

提供示例后添加:

好的,那么创建一个名为 Project 的聚合根怎么样 - 当您从 Repository 构建聚合根时,您可以使用 NetworkData 填充它,操作可能如下所示:

    public class Project
    {
        protected List<Network> networks;
        protected List<NetworkData> networkDatas;

        public void Mutate(string someKindOfNetworkId, object someParam)
        {
            var network = networks.First(n => n.Id == someKindOfNetworkId);
            var someResult = network.DoSomething(someParam);

            networkDatas.Where(d => d.NetworkId == someKindOfNetworkId)
                .ToList()
                .ForEach(d => d.DoSomething(someResult, someParam));
        }
    }

NetworkEditor 不会直接在 Network 上操作,而是通过 Project 使用 NetworkId。

于 2011-08-10T13:56:40.953 回答