我有以下数据库模型:
**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
答案 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。枚举必须映射到状态表的键。这可以使用一个方面来保持原始域模型的清洁。