在DDD上下文中,域模型上的setter是一种代码味道。应该避免它们的原因很简单,因为它们实际上不是域的一部分。其中没有领域专家可以理解的名词,对数据的更改应该通过特定的方法。
示例:
customer.StreetName = ...
customer.City = ...而这样做的正确方法是拥有一个customer.ChangeAddress方法,它可以发布事件等。至少从我的理解来看,这都是合理的理论,我可以完全理解为什么域模型中的setter并不是真正需要的。
但是:如果域模型上没有setter,那么这些方法就很难测试。
如果我既不能构造一个接受所有参数的大构造函数,也不能做一些反射魔法,我如何让一个Customer实例来运行我的测试呢?我在后端使用NHibernate,所以NHibernate已经做了一些反射魔术来填充这些字段。
但是有一个有10个参数的ctor真的很糟糕..(工厂方法也是如此)。
对此有什么建议吗?
问候大牛
发布于 2010-09-16 13:41:22
在传统的(非CQRS) DDD中,将所有数据分解为值对象是一个很好的实践,这样您的实体就可以简化为它们的主要功能:维护身份。
在您的示例中,Customer应该引用一个Address ValueObject并拥有一个ChengeAddress方法,该方法应该像下面这样简单:
public void ChangeAddress(Address address)
{
//Consistency rules are here
_address = address;
}尝试将尽可能多的逻辑从实体转移到值对象。它们本质上更易于测试,因为好的值对象很小,而且是不可变的。您可以使用构造函数在给定状态下实例化VO并执行它(通常通过调用返回另一个已转换的VO实例的方法)。
最后但同样重要的是,根据我的经验,我可以说,如果测试域模型需要额外的基础设施(如反射或任何其他工具),那么您就做错了(通过引入不必要的耦合)。
发布于 2010-09-15 22:41:05
你可能想试试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);你至少应该试着把那些“大块头”的东西分成小的。试着建立一个层次结构(只要确保它符合普遍存在的语言)。
https://stackoverflow.com/questions/3716770
复制相似问题