2

在域模型上的 DDD 设置器的上下文中是代码异味。应该避免使用它们,原因很简单,因为它们实际上并不是域的一部分。其中没有领域专家可以理解的名词,而对数据的更改应该通过特定的方法。

例子:

customer.StreetName = ...
customer.City = ...

虽然这样做的正确方法是拥有一种customer.ChangeAddress可以发布事件等的方法。至少根据我的理解,这都是合理的理论,我完全可以理解为什么领域模型中的设置器并不是真正可取的。

但是:如果您的域模型上没有设置器,这些方法很难测试。

如果我不能构造一个没有接受所有参数的大屁股构造函数或做一些反射魔法的客户实例,我如何让一个客户实例运行我的测试?我在后端使用 NHibernate,所以 NHibernate 已经做了一些反射魔法来填充这些字段。

但是拥有一个有 10 个参数的 ctor 感觉真的很糟糕。(对于工厂方法也是如此)。

对此有何建议?

问候丹尼尔

4

2 回答 2

3

在经典(非 CQRS)DDD 中,将所有数据分解为值对象是一种很好的做法,这样您的实体就可以简化为它们的主要功能:维护身份。

在您的示例中,客户应该引用一个地址 ValueObject 并具有一个 ChengeAddress 方法,该方法应该很简单:

public void ChangeAddress(Address address)
{
   //Consistency rules are here
   _address = address;
}

尝试将尽可能多的逻辑从实体转移到值对象。它们本质上更具可测试性,因为好的价值对象很小且不可变。您使用构造函数在给定状态下实例化 VO 并执行它(通常通过调用返回另一个已转换的 VO 实例的方法)。

最后但并非最不重要的一点是,根据我的经验,我可以说,如果测试您的域模型需要额外的基础设施(如反射或任何其他工具),那么您做错了(通过引入不必要的耦合)。

于 2010-09-16T05:41:21.817 回答
1

您可能想尝试AutoFixture

加入一点反射爱和领域变得相当可测试:

namespace Unit{
  using System;
  using System.Linq.Expressions;
  public static class ObjectExtensions{
    public static T Set<T,TProp>(this T o,
      Expression<Func<T,TProp>> field,TProp value){

      var fn=((MemberExpression)field.Body).Member.Name;
      o.GetType().GetProperty(fn).SetValue(o,value,null);
      return o;
    }
  }
}

用法:

myUberComplexObject.Set(x=>x.PropertyOfIt, newValueOfIt);

而且您至少应该尝试将那些“大屁股”对象分成较小的对象。尝试建立一个层次结构(只要确保它符合普遍存在的语言)。

于 2010-09-15T14:41:05.180 回答