丰富的域模型 - 保护状态与映射的便利性

时间:2014-02-02 09:05:15

标签: oop domain-driven-design

我希望你能解决我与同事之间的轻微分歧。

我一直认为,面向对象设计的原则是对象应该保护其内部状态,并且只公开外部世界需要访问或更改的内容。在富域模型的上下文中,这意味着域对象应该强制它们所代表的模型的有效性,并且它们不应该允许外部调用者使其状态无效,即使它们是核心域对象而不是直接暴露在外面的世界。

以Thing类型的对象为例,它具有一组属性。我的同事提出了以下建议。

public class Thing 
{
    public List<ThingProperty> Properties { get; set; }
}

我不喜欢这个,因为它允许外部调用者将Properties引用设置为null。他的辩护是,如果没有这个可变参考,很难从数据访问中将数据加载到模型中,或者从表示层到核心模型的映射。

我的解决方案如下......

public class Thing
{
    private readonly List<ThingProperty> properties;
    public Thing() { properties = new List<ThingProperty>(); }
    public ReadOnlyCollection<ThingProperty> { get { return properties.AsReadOnly(); } }

    public void AddProperty(ThingProperty add) { (validate) properties.Add(add); }
}

这意味着在向Thing添加属性时可能需要的任何验证都可以在模型内验证(例如,如果只添加给定属性类型的1个实例),并且模型始终确保有效状态。缺点是很难从数据访问或表示模型映射到这种形式,但我认为这是一个值得付出的代价。

有什么想法?感谢。

2 个答案:

答案 0 :(得分:2)

你已经做对了,这就是DDD 不变量的全部内容。通过使实体强制执行其自身的完整性(或聚合根为其聚合强制执行不变量),您可以确保您的域对象为always valid

将数据加载到模型中的论据&#34;是一个奇怪的,因为加载一个无效的实体有一点价值IMO - 一个&#34;有两只眼睛的独眼巨人&#34;或者示例中的Thing为空Properties

当然,如果您的属性是只读的,那么您将无法访问内联对象初始值设定项,但还可以通过其他方式实例化实体(即有效实体):构造函数,工厂,自定义构建器模式等。此外,如果您正在谈论从持久性数据存储中重新水化域对象,大多数ORM都能够操作私有或受保护的字段,因此无需担心

另请注意,函数式编程技术可能会使这更容易实现:immutability by default,非可空类型......

答案 1 :(得分:1)

  

缺点是很难从数据访问或表示模型映射到此表单

但您有一些简化从数据访问到域实体的映射的选项

How to retrieve Domain Object from Repositories

有更详细的答案

Properties时,domain objects enforce the validity of the model they represent集合的实施已接近best practices for read-only lists implementation