我有一个与实体验证有关的问题。例如,有一个User
可以注册到给定email
和password
的系统中。业务规则说:
email
必须有效(必须符合电子邮件格式)且唯一; password
应为6到20个字符。我最初的想法是将验证置于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
相关联。通过这种方式,规则在域中明确显示,使其更具表现力。
所以问题是:你对这种方法有什么看法?还有其他解决方案吗?
答案 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和3是简单的验证,应该可以在实体属性上以声明方式完成(例如,使用.NET中的自定义属性和合适的验证库)。
上面的 2是一个棘手的问题,而且我认为存在对User
存储库的内在依赖性。
问题是:“阻止使用与现有User
相同的电子邮件地址创建User
的责任在于User
实体?”。我相信这个问题的答案是“不”......它“觉得”这个责任应该归功于一个更高级别的服务或实体,对于这个服务或实体来说,了解整个用户是很自然的。
所以,我的看法是:
User
实体内(强大的凝聚力); 答案 2 :(得分:2)
您可以认为有两种验证:内部状态验证和上下文验证。您可以在该实体内执行内部验证,然后使用某些服务执行上下文验证。
答案 3 :(得分:1)
马库斯,
他的方法并不差,但我的做法不同。
在我看来,您尊重OCP,将验证规则排除在实体之外,这是明智的决定。在类构造函数中使用这些验证规则,您建议规则是不可变的,对吧?
我不会这样做,只需创建一个方法dyad将规则设置为此构造函数。对我来说,如果违反验证规则会发生什么并不清楚。我喜欢向处理更普遍警告的用户界面抛出异常。
另一件我不清楚的事情是触发此验证的事件。当实体用户被添加到存储库或者有实体的方法可以做到这一点时?我将采用第二个选项调用方法isValidAuthentication()抛出异常。
关于实体对存储库的依赖性,我冒昧地说这是错误的。您甚至可以让实体依赖于他,因为存储库是对象的集合,这有什么问题?但是,此时似乎很清楚验证是一种服务。因此,如果我们将这些验证放在服务中将消除此耦合并再次应用OCP。你同意吗?
一个大大的拥抱和成功!