2010-02-10 77 views
12

我有以下DB模型:DDD:枚举等实体

**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 

因此,从我的理解,国家就像一个价值对象,用于域逻辑检查。但是它也需要存在于数据库模型中才能表现清晰的ERM。使用这种方法的国家的名字将被硬编码到域

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

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

但是:

所以状态可能会扩展到。

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

问题更新: 也许我的问题还不够清楚。

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

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

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

由于您的国家在数据库中有一个ID,所以它们是实体(值对象由其属性标识,而不是ID)。也许你应该删除它们并将值直接存储在相应的数据库表中? – ZeissS 2010-02-11 14:46:18

回答

7

A previous question of mine出土了一些有用的链接,我怀疑这些链接与你的问题有关,特别是Jimmy Bogard对Enumeration Classes的讨论。

+0

现在使用他的枚举类一段时间...偶然发现升级版本。 https://github.com/HeadspringLabs/Enumeration – CSharper 2015-05-20 19:45:20

+0

也可作为NuGet包使用! - 不错... http://www.nuget.org/packages/Enumeration – rohancragg 2015-06-02 14:20:12

3

这是一个常见的做法包括在枚举值0的“未知”元素。如果你真的想要,你可以做到这一点并将其用于New状态。

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

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设计属于持久层的恕我直言。那里的枚举必须映射到状态表的键。这可以通过使用一个方面来保持原始域模型的清洁。

7

你提出的结构看起来很好。(术语题外话:因为State有一个ID,它不是一个值对象,而是一个实体

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

不必编写

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

在你的代码,这将允许你写

person.State.DoSomething(); 

这是更清洁,而且将允许您如果需要的话加入新的国家。

+0

为什么匿名downvote? – 2010-02-10 14:59:27

+0

问题与如何推动状态模式有关,其给定的提问者正在使用状态模式。 – 2010-02-11 17:24:51

+2

@Brian Leahy:我没有看到问题中任何地方提到的State * pattern * – 2010-02-11 17:53:44

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.

通常将工厂(如this)与状态模式结合使用。

所以,当你从数据库中读取存储的整数值时,你可以将int转换为enum并用它调用工厂以获取适当的State对象。

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"); 
     } 
    } 

现在你已经消除了,如果/ switch语句,你的代码可能仅仅是:

state.DoSomething(); 

我之所以这样做是往往这些类型的实体(或DDD更可能值对象)可以由客户进行配置,即它们可能不希望有一些国家的活跃在系统,或者他们可能希望将他们称为别的东西。通过对属性进行编程,客户可以随意删除/编辑记录,即使该过程生成新的ID也不会影响系统,只需设置属性即可。