Validation DDD中对象验证和持久性验证之间的区别?

Validation DDD中对象验证和持久性验证之间的区别?,validation,domain-driven-design,Validation,Domain Driven Design,现在,我有一个名为StyleBundle的域实体。此样式包包含一个样式列表: public class StyleBundle { public StyleBundle(List<Style> styles) { this.Styles = styles; } public IEnumerable<Style> Styles { get; private set;} } 这确保了我无法在无效状态下创建StyleBundl

现在,我有一个名为StyleBundle的域实体。此样式包包含一个样式列表:

public class StyleBundle
{
    public StyleBundle(List<Style> styles)
    {
        this.Styles = styles;
    }

    public IEnumerable<Style> Styles { get; private set;}
}
这确保了我无法在无效状态下创建StyleBundle。我认为在这里有一个例外是有意义的b/c在系统中创建一个没有至少一个样式的样式包是例外的

当然,在项目的其余部分会发生变化,现在用户可以创建没有样式的样式包,但是不允许他们保留没有样式的样式包

现在我在看我的guard子句,意识到我不能再从构造函数中抛出异常了

向前看,我有一个服务/应用层,我的代码后面的服务/应用层在使用样式包时与之交互。在我的服务层中,我有一个StyleBundleService类,该类向UI公开基本功能。。。其中包括“CreateStyleBundle”

似乎在将样式包持久化到数据库之前,我必须让我的服务层检查样式包是否有任何样式,但我觉得这个决定有些“错误”

有人遇到过类似的事情吗?基本上,一个对象在“新建”时的有效状态与同一个对象在持久化时的状态有什么不同

谢谢!
Mike

我会向您的实体添加一个
IsValid
方法。这将检查实体当前是否处于有效状态(在您的情况下,检查是否存在样式)


可以从
存储库中调用此方法来检查实体是否可以持久化。您可以为特定实体的
IsValid
方法添加更多规则,并且您可以实现类似于验证错误集合的功能,因为您希望抛出有意义的异常。

扩展Wouter所说的内容,以及方便的保存前和删除前方法:

public interface IDomainObject<T>
{
    bool IsValid();
}

public interface IEntity<T> : IDomainObject<T>
{

}

public interface IAggregateRoot<T> : IEntity<T>
{
    void BeforeSaving();
    void BeforeDeleting();
}


public interface IAggregateRoot { //or simply IEntity depending on the model
   bool IsValid();
}

public class StyleBundle : IAggregateRoot<T> {
   return styles.Count() > 0
}

public class StyleBundleRepository : Repository<StyleBundle> {
}

public abstract class Repository<T> : IRepository<T> where T : class, IAggregateRoot<T> {

   public T Save(T t)
   {
      t.BeforeSaving(); //for all AggregateRoots, maybe logging what the aggregate was like before the changes

      if(!t.IsValid())
         throw Exeception("Entity invalid");

      EntityStore.Current.SaveChanges();         

      // "AfterSaving" here, i.e.: log how the entity looks after the update

   }
}
公共接口IDOMAIN对象
{
bool是有效的();
}
公共接口属性:IDomainObject
{
}
公共接口IAggregateRoot:通用性
{
保存前作废();
删除前作废();
}
公共接口IAggregateRoot{//或简单地说,取决于模型
bool是有效的();
}
公共类StyleBundle:IAggregateRoot{
返回样式。计数()>0
}
公共类StyleBundleRepository:存储库{
}
公共抽象类存储库:IRepository,其中T:class,IAggregateRoot{
公共T存储(T T)
{
t、 BeforeSaving();//对于所有AggregateRoot,可能会记录更改前聚合的情况
如果(!t.IsValid())
抛出异常(“实体无效”);
EntityStore.Current.SaveChanges();
//此处为“AfterSaving”,即:记录实体在更新后的外观
}
}

编辑:我个人不使用IsValid这个概念,我使用一个完整的EntityValidationErrors类,在尝试保存之前,我可以向客户端报告错误,不应该为空,不应该为空(如您的样式等)

有多种策略:

一些开发人员更喜欢在实体本身中创建两个方法,一个名为
IsValid()
,用于根据业务规则验证实体(常规验证),另一个名为
IsValidForPersistence()
,用于验证实体的持久性

关于
IsValid()
,我宁愿首先不允许通过验证所有输入而出现无效状态,并使用factory或builder来支持不变量

你可以查看链接
想一想。

我知道,这个问题已经问了三年了,但看到现在的答案是我想回答的。我们正在讨论域数据。因此,不可能存在包含0个对象的有效StyleBundle。我想,你在某个地方有一个前端编辑器,你是否创建了一个“新”样式包,并且在点击“保存”按钮之前必须添加至少一个样式

在前端的这一点上,您将没有域对象。您可能有一个数据传输对象,该对象将通过“CreateNewStyleBundle”命令发送

在我看来,域对象必须是不可知的,并且应该始终处于有效状态。如果必须调用“IsValid”方法,那么首先就可以避免使用域对象


这只是我的拙见。

存储库检查实体的有效性是好主意还是坏主意?我觉得有点奇怪。但不管怎样,我都喜欢.IsValid这个概念。谢谢你的反馈!我不认为在您的存储库中进行最新检查是个坏主意。否则,用户可能会向数据库中添加一些无法捕获的无效数据。正如Momamed Abed所建议的,您可以将其分为IsValid和IsValidForPersistence方法。您认为存储库对实体进行有效性检查是好主意还是坏主意?我可以想象,您想让存储库保持“哑巴”,让服务层调用验证代码?这可以确保没有任何内容被持久化为无效,无论代码的其他部分做什么,实体本身是唯一知道它无效的实体,存储库只是在强化契约。我做了一些编辑,可能是在你阅读之后,为了进一步的想法,Asf.Aquino,非常感谢你的努力和代码。我要看看在接下来的几天里,我是否能在我的模型中加入这样的东西。在那之后我会回到马克接受的答案。穆罕默德,谢谢你的链接。该验证实现非常复杂,我相信有更简单的方法可以在聚合根上实现.IsValid()和.IsValidForPersistence()。我还认为,使用Factory或Builder是一种不允许实体达到无效状态的好方法,b/c在构建实体之前,您仍然可以向用户/应用层返回有用的消息。除非你提出一个例外
public interface IDomainObject<T>
{
    bool IsValid();
}

public interface IEntity<T> : IDomainObject<T>
{

}

public interface IAggregateRoot<T> : IEntity<T>
{
    void BeforeSaving();
    void BeforeDeleting();
}


public interface IAggregateRoot { //or simply IEntity depending on the model
   bool IsValid();
}

public class StyleBundle : IAggregateRoot<T> {
   return styles.Count() > 0
}

public class StyleBundleRepository : Repository<StyleBundle> {
}

public abstract class Repository<T> : IRepository<T> where T : class, IAggregateRoot<T> {

   public T Save(T t)
   {
      t.BeforeSaving(); //for all AggregateRoots, maybe logging what the aggregate was like before the changes

      if(!t.IsValid())
         throw Exeception("Entity invalid");

      EntityStore.Current.SaveChanges();         

      // "AfterSaving" here, i.e.: log how the entity looks after the update

   }
}