我正在使用域驱动的设计版本来重写我的ASP.NET MVC应用程序.我试图验证我的用户实体.到目前为止,我可以验证基本规则(如用户名和密码是非空/空白字符串).然而,其中一个规则,我需要确保用户名是唯一的.然而,我需要访问数据库才能做到这一点,这意味着我必须将我的IUserRepository注入我的用户实体.
public class User { private readonly IUserRepository _userRepository; public User(IUserRepository repo) { _userRepository = repo; } public override void Validate() { //Basic validation code if (string.IsNullOrEmpty(Username)) throw new ValidationException("Username can not be a null or whitespace characters"); if (string.IsNullOrEmpty(Password)) throw new ValidationException("Password can not be a null or whitespace characters"); //Complex validation code var user = _userRepository.GetUserByUsername(Username); if (user != null && user.id != id) throw new ValidationException("Username must be unique") } }
不过这似乎是错的.使我的实体依赖于我的存储库似乎是一个坏主意(如果我错了,纠正我).但在实体中具有验证码是有意义的.放置复杂验证码的最佳位置在哪里?
解决方法
However this seems … well wrong. Making my entity depend on my repository seems like a bad idea (correct me if I am wrong).
一般来说,对存储库的依赖不是“错误的”,它有时是不可避免的.不过我觉得应该是一个例外,应尽可能避免.在您的情况下,您可能会重新考虑有这种依赖性.如果您想到这一点,“唯一性”不是实体本身的责任,因为实体不了解其他实体.那么为什么要实体执行这个规则呢?
But having the validation code in the entity makes sense. Where is the best place to put complex validation code?
我认为你可能会过度的“验证”.我将摆脱“验证”方法,并确保该对象首先不会进入“无效”状态.几个月前我也是类似的问题answered.
现在回到唯一性规则.我认为这是DDD“漏洞”的一个例子,在某种意义上说,这种业务规则的执行不能纯粹在域代码中表达.我会这样接近:
// repository interface Users { // implementation executes sql COUNT in case of relation DB bool IsNameUnique(String name); // implementation will call IsNameUnique and throw if it fails void Add(User user); }
客户端代码将会知道,在添加新用户之前,应该明确地检查唯一性,否则会崩溃.该组合在域代码中强制执行业务规则,但通常是不够的.作为附加的强制层,您可能希望在数据库中添加UNIQUE约束或使用显式锁定.