DDD实体验证

时间:2012-03-10 12:37:52

标签: validation domain-driven-design

我有一个与实体验证有关的问题。例如,有一个User可以注册到给定emailpassword的系统中。业务规则说:

  1. email必须有效(必须符合电子邮件格式)且唯一;
  2. password应为6到20个字符。
  3. 我最初的想法是将验证置于User.Register(email, password)内。这种方法的主要优点是User通过验证注册数据的正确性来控制注册方式。缺点是电子邮件唯一性验证需要调用UserRepository,因此User可能依赖于其Repository。要解决此问题,可能会将某些BusinessRule个对象的电子邮件和密码验证考虑在内。因此User.Register()方法中的验证可能如下所示:

    var emailValidationErrors = _emailRule.Validate(email);
    var passwordValidationErrors = _passwordRule.Validate(password);
    

    其中_emailRule_passwordRule可能作为构造函数参数传递:User(EmailRule emailRule, PasswordRule passwordRule).

    此casse User未直接与UserRepository相关联。通过这种方式,规则在域中明确显示,使其更具表现力。

    所以问题是:你对这种方法有什么看法?还有其他解决方案吗?

4 个答案:

答案 0 :(得分:4)

您可以实施封装此功能的域名服务。通常在DDD中,当业务逻辑超出单个聚合的范围时,您将使用域服务;在这种情况下,它是唯一性检查。那么,我要做的是:

public class UserRegistrationService : IUserRegistrationService
{
   private readonly IUserRespository _userRepository;

   public void Register(string email, string password)
   {
        if (!_userRepository.DoesEmailExist(email))
           throw new Exception("Email already registered");

        User user = User.Create(email, password);

        _userRepository.Save(user);
   }
}

此外,如果您担心在注册服务之外调用User.Create并因此转义唯一性检查,您可以将User.Create方法设置为internal,这意味着创建用户的唯一方法是通过RegistrationService。

答案 1 :(得分:3)

在此示例中,您尝试执行三种验证:

  1. 电子邮件地址必须是有效格式;
  2. 电子邮件地址必须是唯一的(即,没有现有用户拥有该电子邮件地址);
  3. 密码必须符合某些长度限制。
  4. 上面的1和3是简单的验证,应该可以在实体属性上以声明方式完成(例如,使用.NET中的自定义属性和合适的验证库)。

    上面的

    2是一个棘手的问题,而且我认为存在对User存储库的内在依赖性。

    问题是:“阻止使用与现有User相同的电子邮件地址创建User的责任在于User实体?”。我相信这个问题的答案是“不”......它“觉得”这个责任应该归功于一个更高级别的服务或实体,对于这个服务或实体来说,了解整个用户是很自然的。

    所以,我的看法是:

    1. 将与用户坐在一起的验证放在User实体内(强大的凝聚力);
    2. 将唯一性约束放在专门负责维护用户集的不变量的DDD服务中 - 它将通过包装唯一性检查和新{{em>的持久性来执行此操作1}}在交易中。

答案 2 :(得分:2)

您可以认为有两种验证:内部状态验证和上下文验证。您可以在该实体内执行内部验证,然后使用某些服务执行上下文验证。

答案 3 :(得分:1)

马库斯,

他的方法并不差,但我的做法不同。

在我看来,您尊重OCP,将验证规则排除在实体之外,这是明智的决定。在类构造函数中使用这些验证规则,您建议规则是不可变的,对吧?

我不会这样做,只需创建一个方法dyad将规则设置为此构造函数。对我来说,如果违反验证规则会发生什么并不清楚。我喜欢向处理更普遍警告的用户界面抛出异常。

另一件我不清楚的事情是触发此验证的事件。当实体用户被添加到存储库或者有实体的方法可以做到这一点时?我将采用第二个选项调用方法isValidAuthentication()抛出异常。

关于实体对存储库的依赖性,我冒昧地说这是错误的。您甚至可以让实体依赖于他,因为存储库是对象的集合,这有什么问题?但是,此时似乎很清楚验证是一种服务。因此,如果我们将这些验证放在服务中将消除此耦合并再次应用OCP。你同意吗?

一个大大的拥抱和成功!