DDD中的空实体

时间:2015-05-13 12:05:22

标签: c# null domain-driven-design nullable

产品实体或聚合为例,该实体或聚合引用另一个名为类别的实体。在发生 CategorizeProduct 等域事件之前,类别的状态为空。要在C#中表示,我必须使用NULL。我不能在C#中使用Nullable<T>,因为它只能用于值类型。

在DDD中使用NULL是反模式吗?已经提出的一些解决方案是(1)使用MayBe<T>(2)在单个实体的位置使用collection

MayBe<t>是一个选项,但有没有不同的方法来处理DDD中的空实体状态?

2 个答案:

答案 0 :(得分:3)

对于这些类型的事物,您可以使用null object pattern。想想例如String.Empty属性就是一个简单的例子。

如果你的null对象的某些方法应该有不同的行为,你可以使它成为一个派生自Category的私有内部类,并覆盖特定的行为。

对于Category

public class Category
{
    private class UnassignedCategory : Category
    {
        public UnassignedCategory() : base("") { }
    }

    private static readonly UnassignedCategory _unassigned = new UnassignedCategory();

    public static Category Unassigned 
    { 
        get { return _unassigned; }
    }

    public Category(string name) 
    {
        this.Name = name;
    }

    public string Name { get; private set; }

    public bool IsAssigned 
    {
        get { return ReferenceEquals(this, _unassigned); }
    }
}

请注意,在上面的示例中,由于Category上的默认无参数构造函数不存在,并且无法更改Name属性的值,因此不需要子类,我们可以简单地使用了静态Category实例。

用法:

public class Product
{
    public Product()
    {
        Category = Category.Unassigned;
        // other stuff
    }

    // other stuff.

    public void AssignCategory(Category category) 
    {
        // Any associated logic.
        Category = category;
    }
    public Category Category { get; private set; } 
}

如果要从数据库中保存并加载null(即未分配的)类别,您可能希望对此主题使用变体,并为保存的“未分配”显式(不可变)id /属性与其他Category属性一起使用,并在IsAssigned检查中使用。

可行的替代方案

一个完全可行的替代方案就是使用null引用继续做你现在正在做的事情。您可以添加一些扩展方法,以便在需要时让您的生活更轻松。

public static MyCategoryExtensions
{
    public static bool IsNullOrEmpty(this Category category) 
    {
        return category == null || string.IsNullOrEmpty(category.Name);
    }
    public static Category EmptyIfNull(this Category category) 
    {
        return category ?? new Category("");
    }
    public static Category DefaultIfNullOrEmpty(this Category category) 
    {
        return category.IsNullOrEmpty() 
            ? new Category(Category.DefaultName)
            : category;
    }
}

答案 1 :(得分:2)

我理解您的担忧 - NULL可能意味着该类别尚未加载或可能意味着尚未分配类别。

一个选项是添加一个可为空的属性CategoryId,它包含已分配的类别的ID。如果指定了类别,则始终填充ID,否则为空。这意味着如果填写了CategoryID且Category为null,则分配了一个类别但未加载。

如果您使用Entity Framework加载或保留您的域,则此方法很自然。

 public class Product
 {
      public Product()
      {
          // validate entity 
      }

      public int? CategoryId { get; private set; }
      public Category Category { get; private set; } 
 }