C# 如何设计处理(真实)合同的数据模型?

C# 如何设计处理(真实)合同的数据模型?,c#,nhibernate,domain-driven-design,C#,Nhibernate,Domain Driven Design,我正在寻找一些关于设计合同管理数据模型的建议。因此,合同的一般生命周期为: 合同已创建并处于“草稿”状态。它在内部可见,可以进行更改 合同发送给供应商,状态设置为“待定” 供应商拒绝了合同。在这种情况下,合同无法执行。不能将任何状态添加到集合中 卖方接受合同。在这种情况下,合同无法执行。不能将任何状态添加到集合中 我显然想避免合同被接受的情况,比如说,金额发生变化。以下是我的课程: [EnforceNoChangesAfterDraftState] public class VendorCo

我正在寻找一些关于设计合同管理数据模型的建议。因此,合同的一般生命周期为:

  • 合同已创建并处于“草稿”状态。它在内部可见,可以进行更改
  • 合同发送给供应商,状态设置为“待定”
  • 供应商拒绝了合同。在这种情况下,合同无法执行。不能将任何状态添加到集合中
  • 卖方接受合同。在这种情况下,合同无法执行。不能将任何状态添加到集合中
我显然想避免合同被接受的情况,比如说,金额发生变化。以下是我的课程:

[EnforceNoChangesAfterDraftState]
public class VendorContract 
{
 public virtual Vendor Vendor { get; set; }            
 public virtual decimal Amount { get; set; }
 public virtual VendorContact VendorContact { get; set; }              
 public virtual string CreatedBy { get; set; }
 public virtual DateTime CreatedOn { get; set; }
 public virtual FileStore Contract { get; set; }

 public virtual IList<VendorContractStatus> ContractStatus { get; set; } 
}        

[EnforceCorrectWorkflow]
public class VendorContractStatus
{
  public virtual VendorContract VendorContract { get; set; }
  public virtual FileStore ExecutedDocument { get; set; }
  public virtual string Status { get; set; }
  public virtual string Reason { get; set; }
  public virtual string CreatedBy { get; set; }
  public virtual DateTime CreatedOn { get; set; }
}
[EnforceNoChangesAfterDraftState]
公共类卖方合同
{
公共虚拟供应商{get;set;}
公共虚拟十进制数{get;set;}
公共虚拟VendorContact VendorContact{get;set;}
由{get;set;}创建的公共虚拟字符串
公共虚拟日期时间CreatedOn{get;set;}
公共虚拟文件存储协定{get;set;}
公共虚拟IList ContractStatus{get;set;}
}        
[强制执行工作流]
公共类卖方合同状态
{
公共虚拟卖方合同卖方合同{get;set;}
公共虚拟文件存储已执行文档{get;set;}
公共虚拟字符串状态{get;set;}
公共虚拟字符串原因{get;set;}
由{get;set;}创建的公共虚拟字符串
公共虚拟日期时间CreatedOn{get;set;}
}
我省略了filestore类,它基本上是一个键/值查找,用于根据其guid查找文档

供应商合同状态在Nhibernate中映射为多对一

然后,我使用一个自定义验证器,如前所述。如果VendorContractStatus集合中返回的不是草稿,则不允许进行任何更改。此外,VendorContractStatus必须遵循正确的工作流(您可以在挂起后添加拒绝,但如果存在拒绝或接受,则无法向集合中添加任何其他内容,等等)

听起来还好吧?一位同事认为,我们应该简单地向VendorContract添加一个“IsDraft”bool属性,如果IsDraft为false,则不接受更新。然后我们应该在VendorContractStatus内部设置一个方法来更新状态,如果在草稿之后添加了一些内容,它会将VendorContract的IsDraft属性设置为false

我不喜欢这样,因为我觉得我正在弄脏POCO,并添加应该在验证区域中保持的逻辑,这些类中不应该真正存在任何规则,它们不应该知道它们的状态

从DDD的角度来看,有什么想法和更好的做法吗

在我看来,如果将来我们想要更复杂的规则,从长远来看,我的方法将更易于维护。假设我们有超过一定金额的合同,需要经理批准。我认为最好使用VendorContractApproval类进行一对一映射,而不是添加IsApproved属性,但这只是猜测


这可能令人毛骨悚然,但这是我们完成的第一个真正坚韧不拔的企业软件项目。任何建议都将不胜感激

在调用方尝试持久化
VendorContract
实例之前,规则是不会强制执行的,这一事实可能会增加bug的可能性,并使这些类更难使用,因为它们不描述自己的行为或约束

换言之,我们只关注一个特性:拒绝和接受的合同甚至不应该为
Amount
设置访问器


听起来您可能正在走向一个(有时称为“不那么有偏见的名称”),其中您的域对象只是没有业务逻辑的容器。这是一个问题,但在具有大量业务逻辑的系统中,样式可能会重复代码,并将规则分散到太多的对象上,这使得理解(以及维护和更改)管理域实体的规则非常困难。

不要这样做。编写描述所需行为的测试。当您为所有用例(以及重构)编写测试时,数据模型就完成了。

两件要考虑的事情; 1) 您真的希望合同请求和生效合同使用相同的对象模型吗。他们不会有不同的用例应用于他们吗


2) 如果您确实希望使用同一对象,则可以使用来控制哪些操作对当前状态有效。

谢谢。我同意这一点,我认为我们决定将这些类分为VendorContractDraft和VendorContract,因为从验证的角度来看,这是两种状态。这样,我们可以更自然地使用验证逻辑标记属性,而不是在单独的验证类中进行标记。我至少会从VendorContract派生一个AcceptedVendorContract,它在代码中是不可变的。AVC可以使用update触发器写入同一个表,防止更新标记为accepted的记录,或者写入程序的SQL用户只有创建权限的其他表。Keith,如果可能的话,我会避免将数据库用于业务逻辑。更难测试,并且您将在层之间拆分应用程序逻辑;一旦标记为accepted,VendorContract就会进入一个用户程序无权修改的新表,并被检索到一个甚至不可变的新对象中。这对于将其放入NHib支持的存储库来说是微不足道的。