DDD:枚举像实体一样

时间:2010-02-10 14:22:17

标签: c# .net design-patterns domain-driven-design

我有以下数据库模型:

**Person table**
ID    |    Name    | StateId
------------------------------
1          Joe       1
2          Peter     1
3          John      2

**State table**
ID    |    Desc
------------------------------
1          Working
2          Vacation

和域模型将(简化):

public class Person
{
    public int Id { get; }
    public string Name { get; set; }
    public State State { get; set; }
}

public class State
{
    private int id;
    public string Name { get; set; }
}

状态可能在域逻辑中使用,例如:

if(person.State == State.Working)
    // some logic

因此,根据我的理解,State就像一个用于域逻辑检查的值对象。但它也需要存在于DB模型中以表示干净的ERM。

所以状态可能扩展到:

public class State
{
    private int id;
    public string Name { get; set; }

    public static State New {get {return new State([hardCodedIdHere?], [hardCodeNameHere?]);}}
}

但是使用这种方法,状态名称将被硬编码到域中。

你知道我的意思吗?对于这样的事情,有没有标准的方法?从我的角度来看,我想要做的是使用一个对象(从ERM设计角度来看是持久的)作为我域中的一种值对象。你觉得怎么样?

问题更新: 可能我的问题不够明确。

我需要知道的是,我将如何使用存储在域逻辑中的数据库中的实体(如State示例)。为了避免这样的事情:

  if(person.State.Id == State.Working.Id)
      // some logic

if(person.State.Id == WORKING_ID)
// some logic

6 个答案:

答案 0 :(得分:8)

previous question of mine我发现了一些我怀疑与你的问题有关的有用链接,特别是Jimmy Bogard关于Enumeration Classes的讨论。

答案 1 :(得分:7)

您建议的结构似乎很好。 (术语题外话:由于State有ID,因此它不是值对象,而是实体。)

枚举是一种代码气味,所以不要试图走那条路。使用State模式将行为移动到State对象中更加面向对象。

而不是写

if (person.State == State.Working)
    // do something...

遍布您的代码,这将允许您编写

person.State.DoSomething();

更清洁,如果需要,可以添加新状态。

答案 2 :(得分:3)

通常的做法是在枚举中包含值为0的“未知”元素。如果你真的想要,你可以这样做并将它用于New状态。

但是您所描述的是业务逻辑......在创建新对象之后设置状态应该在业务逻辑层中进行,而不是在类本身内部。

答案 3 :(得分:2)

您希望创建一个工厂方法,根据存储的值实例化所需的相应状态类。

类似

public  static State GetStateByID( StateEnum value)
{
   if(value.Invalid)
        throw new Exception();

switch(value)
   case State.Working
        return new WorkingState();
   case State.somethingelse
         return new somethingelseState();
   case State.something
         return new somethingState();
   case State.whatever
         return new whateverState();

}

使用枚举时,请始终尝试使用0作为无效。在引擎盖下,枚举是值类型,未分配的int总是0。

通常使用像这样的工厂和状态模式。

因此,当您从数据库中读取存储的整数值时,可以将int转换为枚举并使用它调用工厂以获取相应的State对象。

答案 4 :(得分:1)

我个人认为对ID进行编程是错误的。相反,我会将您的表修改为以下内容:

   **State table**
ID    |    Desc               | IsWorking  | IsVacation
-----------------------------------------------------------
1          Working                True         False
2          Vacation               False        True

然后我会使用这些属性来制定业务决策,例如:

    public void MakeDecisionOnState(State state)
    {
        if (state.IsVacation)
            DoSomething();
        if (state.IsWorking)
            DoSomethingElse();
    }

或者通过更聪明,使用工厂模式根据这些属性创建正确的实例:

    public abstract class State
    {
        public Guid Id { get; set; }

        public string Description { get; set; }

        public abstract void DoSomething();

    }

    public class WorkingState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the working state
        }
    }

    public class VacationState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the vacation state
        } 
    }

    public class StateFactory
    {
        public static State CreateState(IDataRecord record)
        {
            if (record.GetBoolean(2))
                return new WorkingState { Id = record.GetGuid(0), Description = record.GetString(1) };
            if (record.GetBoolean(3))
                return new VacationState { Id = record.GetGuid(0), Description = record.GetString(1) };

            throw new Exception("Data is screwed");
        }
    }

现在你已经删除了if / switch语句,你的代码可能只是:

state.DoSomething();

我这样做的原因是,客户通常可以配置这些类型的实体(或更可能是DDD中的值对象),即他们可能不希望系统中的某些状态处于活动状态,或者他们是不妨给别人说些别的话。通过对属性进行编程,客户可以随意删除/编辑记录,即使该进程生成新ID,也不会影响系统,他们只需要设置属性。

答案 5 :(得分:0)

在我的观点中,域层必须与DB模型/ ERM设计分开。我很难理解你对国家班的最终建议。恕我直言,这对于建立一种共同语言并不是一件好事,这是DDD的主要目的之一。

我会选择更简单的设计。该州属于Person类。我会把它包括在课堂上。

public class Person
{
    public int Id { get; }
    public string Name { get; set; }
    public PersonState State { get; set; }
}

州政府本身似乎已经定义了价值观(我假设一个人是您上下文中的一名员工),这些价值观不会经常发生变化。所以我将它建模为枚举并将其视为数据类型。

enum Days {Working, Vacation};

在我看来,这是一个简单易懂的设计。 ERM设计的映射属于持久层中的IMHO。枚举必须映射到状态表的键。这可以使用一个方面来保持原始域模型的清洁。