Ste*_*aly 9 c# validation asp.net-mvc domain-driven-design
我正在使用域驱动设计原则重写我的ASP.NET MVC应用程序.我正在尝试验证我的用户实体.到目前为止,我能够验证基本规则(例如用户名和密码是非null /空格字符串).但是其中一条规则,我需要确保用户名是唯一的.但是我需要访问数据库才能执行此操作,这意味着我必须将IUserRepository注入我的User实体中.
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")
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这似乎......错了.让我的实体依赖于我的存储库似乎是一个坏主意(如果我错了,请纠正我).但是在实体中使用验证代码是有道理的.放置复杂验证码的最佳位置在哪里?
我在这些类型的情况中使用的模式是将这种类型的验证逻辑放在应用程序服务中.在某种程度上,这是有道理的,因为User实体只负责其自身的有效性,而不是用户集的有效性.创建用户的应用程序服务方法可能如下所示:
public User CreateUser(string userName)
{
if (this.userRepository.Exists(userName))
throw new Exception();
var user = new User(userName);
this.userRepository.Add(user);
return user;
}
Run Code Online (Sandbox Code Playgroud)
无论您是使用DDD还是不使用DDD,应用程序服务都是一种抽象,因此当DDD产生摩擦时,它是一个可以回归的好地方.
然而,这似乎......错了.让我的实体依赖于我的存储库似乎是一个坏主意(如果我错了,请纠正我).
一般来说,对存储库的依赖不是"错误的",有时候是不可避免的.但是我认为它应该是一个例外,应尽可能避免.在您的方案中,您可能会重新考虑具有此依赖性.如果你考虑一下,"唯一性"不是实体本身的责任,因为实体不了解其他实体.那么为什么让实体执行这条规则呢?
但是在实体中使用验证代码是有道理的.放置复杂验证码的最佳位置在哪里?
我认为你可能会过度概括'验证'.我将摆脱'Validate'方法,并确保对象首先不会进入'无效'状态.我几个月前回答了类似的问题.
现在回到唯一性规则.我认为这是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);
}
Run Code Online (Sandbox Code Playgroud)
客户端代码知道在添加新用户之前,它应该明确检查唯一性,否则它将崩溃.此组合在域代码中强制执行业务规则,但这通常是不够的.作为附加的强制层,您可能希望在数据库中添加UNIQUE约束或使用显式锁定.
然而这似乎……错了
不,这根本没有错。让域模型依赖于存储库是完全可以的。除此之外,您还可以将存储库抽象到接口后面,这甚至更好。
或者不要使用构造函数注入。如果唯一需要存储库的方法,请将存储库传递给 Validate 方法:
public class User
{
public void Validate(IUserRepository repo)
{
//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 = repo.GetUserByUsername(Username);
if (user != null && user.id != id)
throw new ValidationException("Username must be unique")
}
}
Run Code Online (Sandbox Code Playgroud)