从贫血领域到领域驱动

时间:2014-06-04 10:20:27

标签: c# entity-framework design-patterns anemic-domain-model

我试图找到一个清晰而简单的例子,说明贫血领域的真正含义。有很多理论,还有很多很好的答案。尽管如此,我无法清楚地了解到什么程度的贫血领域"意思真的如此。因此,我相信看到一个贫血领域设计的虚拟实际例子会更简单,而不是问你怎么可能演变成一个域驱动的...

所以,我们假设我们有一个 TaskData 类型的数据实体:

public class TaskData
{
    public Guid InternalId { get; set; }

    public string Title { get; set; }

    public string Details { get; set; }

    public TaskState ExplicitState { get; set; }

    public IEnumerable<TaskData> InnerTasks { get; set; }

}

还需要一个名为&#34; ActualState &#34;的附加属性,这是一个计算状态:如果任务有内部子任务,则该值严格依赖于孩子,否则,&#34; ActualState &#34;等于&#34; ExplicitState &#34;

如果我在一个单独的服务类中编写此逻辑(我称之为&#34; 引擎&#34;),我们有:

internal class TaskStateCalculator
{
    public TaskState GetState(TaskData taskData)
    {
        if (taskData.InnerTasks.Any())
        {
            if (taskData.InnerTasks.All(x => this.GetState(x) == TaskState.Done))
            {
                return TaskState.Done;
            }
            if (taskData.InnerTasks.Any(x => this.GetState(x) == TaskState.InProgress))
            {
                return TaskState.InProgress;
            }

            return TaskState.Default;
        }

        return taskData.ExplicitState;
    }       
}

第一个问题是:

上面的代码是否反映了贫血的域设计,即使 TaskStateCalculator 服务/引擎是我的域层的一部分? 如果是,为了避免它,我们需要在 TaskData 类中移动逻辑(并将 TaskData 重命名为 Task )。我是对的吗?

第二个问题是(实际上是他们的链):

如果我们遇到更困难的情况怎么办?让我们说在任务实体中需要一个名为 ComputeSomething 的属性,并且该属性的逻辑需要访问整个任务< EM>存储库。在这种情况下,任务类将依赖于 TaskRepository 。这样可以吗? EF将如何构建此类的实例?有什么替代方案?

1 个答案:

答案 0 :(得分:6)

  

我试图找到一个清晰而简单的例子,说明贫血领域的真正含义

事实上,从贫血领域模式到富裕领域模型实际上很容易。

  1. 将所有属性设置器设置为private,然后在要更改模型状态时添加方法。
  2. 评估所有Law of Demeter违规行为,并在合适的地方添加方法。
  3. 最终你会有一个正确的模型。

    在你的情况下,我会将这个逻辑封装在TaskData中,因为你的TaskStateCalculator违反了得墨忒耳法

    public class TaskData
    {
        public Guid InternalId { get; private set; }
    
        public string Title { get; private set; }
    
        public string Details { get; private set; }
    
        public TaskState ExplicitState { get; private set; }
    
        public IEnumerable<TaskData> InnerTasks { get; private set; }
    
        public TaskState GetState()
        {
            if (!InnerTasks.Any())
                return ExplicitState;
    
            if (InnerTasks.All(x => this.GetState(x) == TaskState.Done))
            {
                return TaskState.Done;
            }
    
            if (InnerTasks.Any(x => this.GetState(x) == TaskState.InProgress))
            {
                return TaskState.InProgress;
            }
    
            return TaskState.Default;
        }       
    }
    

    另一件事是我可能根本不会将InnerTasks集合暴露给外界(只是将它作为成员字段)。但我很难说,因为我不知道在其他场景中如何使用该类。

    为何选择私人制定者

    每次你必须改变多个属性时,用方法描述行为往往更好,因为它不可能忘记改变所有必需的属性。与更改一组属性相比,方法还更好地描述了您要做的事情。

    即使您只更改了一个属性,该属性也可以将类设置为无效状态,因为更改可能与类中的其余信息不兼容。别忘了封装是OOP的核心原则之一