我是DDD的新手,并且正在尝试将其应用于现实生活中。毫无疑问,此类验证逻辑(如空检查,空字符串检查等)直接进入实体构造函数/属性。但是,如何验证某些全球规则(例如“唯一用户名”)呢?
因此,我们有实体用户
public class User : IAggregateRoot
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// other data and behavior
}
和用户存储库
public interface IUserRepository : IRepository<User>
{
User FindByName(string name);
}
选项有:
并且每个选项都更加详细:
1。将存储库注入实体
我可以查询实体构造函数/属性中的存储库。但是我认为在实体中引用存储库是一种难闻的气味。
public User(IUserRepository repository)
{
_repository = repository;
}
public string Name
{
get { return _name; }
set
{
if (_repository.FindByName(value) != null)
throw new UserAlreadyExistsException();
_name = value;
}
}
更新:我们可以使用DI通过Specification对象隐藏User和IUserRepository之间的依赖关系。
2.将存储库注入工厂
我可以将此验证逻辑放在UserFactory中。但是,如果我们想更改现有用户的名称怎么办?
3.在域服务上创建操作
我可以创建用于创建和编辑用户的域服务。但是有人可以直接编辑用户名而无需调用该服务…
public class AdministrationService
{
private IUserRepository _userRepository;
public AdministrationService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void RenameUser(string oldName, string newName)
{
if (_userRepository.FindByName(newName) != null)
throw new UserAlreadyExistException();
User user = _userRepository.FindByName(oldName);
user.Name = newName;
_userRepository.Save(user);
}
}
4。???
您将实体的全局验证逻辑放在哪里?
谢谢!
在大多数情况下,最好将此类规则放在Specification
对象中。您可以将这些Specification
放入您的域包中,因此任何使用您的域包的人都可以访问它们。使用规范,您可以将业务规则与实体捆绑在一起,而无需创建对服务和存储库具有不期望依赖性的难以阅读的实体。如果需要,可以将对服务或存储库的依赖项注入到规范中。
根据上下文,您可以使用规范对象构建不同的验证器。
实体的主要关注点应该是跟踪业务状态-这足以承担责任,并且他们不应该关注验证。
例
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
两种规格:
public class IdNotEmptySpecification : ISpecification<User>
{
public bool IsSatisfiedBy(User subject)
{
return !string.IsNullOrEmpty(subject.Id);
}
}
public class NameNotTakenSpecification : ISpecification<User>
{
// omitted code to set service; better use DI
private Service.IUserNameService UserNameService { get; set; }
public bool IsSatisfiedBy(User subject)
{
return UserNameService.NameIsAvailable(subject.Name);
}
}
和验证器:
public class UserPersistenceValidator : IValidator<User>
{
private readonly IList<ISpecification<User>> Rules =
new List<ISpecification<User>>
{
new IdNotEmptySpecification(),
new NameNotEmptySpecification(),
new NameNotTakenSpecification()
// and more ... better use DI to fill this list
};
public bool IsValid(User entity)
{
return BrokenRules(entity).Count() > 0;
}
public IEnumerable<string> BrokenRules(User entity)
{
return Rules.Where(rule => !rule.IsSatisfiedBy(entity))
.Select(rule => GetMessageForBrokenRule(rule));
}
// ...
}
为了完整起见,这些接口:
public interface IValidator<T>
{
bool IsValid(T entity);
IEnumerable<string> BrokenRules(T entity);
}
public interface ISpecification<T>
{
bool IsSatisfiedBy(T subject);
}
笔记
我认为Vijay
Patel的较早答案是朝正确的方向发展,但我觉得有些偏离。他建议用户实体取决于规范,我认为应该与规范相反。这样,您可以使规范通常依赖于服务,存储库和上下文,而无需通过规范依赖关系使您的实体依赖于它们。
参考文献
埃里克·埃文斯(Eric Evans)在第9章145页中介绍了使用规范模式进行验证,选择和对象构建。
该文章的规格模式:在.NET应用程序可能是你的兴趣。
我将在Spring中使用DDD构建一个新应用程序。我将有一个REST适配器、一个JPA适配器和我的域模型。 我的问题是在哪里进行现场验证?假设我有一个REST方法来下订单,我应该在哪里验证请求中的订单数量是否大于0?在REST适配器的DTO中?或者在我的域实体中,因为验证应该是业务逻辑的一部分? 如果我在REST请求的DTO中进行验证,我就可以将验证检查添加到DTO的字段中,并在我的REST控制器
验证规则的定义通常有两种方式,如果你使用了验证器的话,通常通过rule属性定义验证规则,而如果使用的是独立验证的话,则是通过rule方法进行定义。 属性定义 属性定义方式仅限于验证器,通常类似于下面的方式: <?php namespace app\index\validate; use think\Validate; class User extends Validate { prot
我正在尝试验证密码字段是否存在。我想允许某人编辑用户,他们可能想更改用户密码,也可能不想更改用户密码。所以我想我可以使用Laravels验证规则,特别是“有时”规则来实现这一点。我有一套规则: 例如,对于其他字段,通常会有其他规则,而对于密码,则会有更严格的规则。如果传递的数据中存在密码字段,我希望这只应用min:8规则,但是如果我将密码字段留空,则会出现一个验证错误,表明密码字段是必需的。 我不
如何在验证器中添加密码验证规则? 验证规则: 密码包含以下五个类别中至少三个类别的字符: 英文大写字符(A–Z) 英文小写字符(a–z) 以10位数字(0–9)为基数 非字母数字(例如:!、$、#或%) Unicode字符 如何在验证器规则中添加上述规则? 我的代码在这里
本文向大家介绍PHP Yii框架之表单验证规则大全,包括了PHP Yii框架之表单验证规则大全的使用技巧和注意事项,需要的朋友参考一下 Yii是一个基于组件的高性能PHP框架,用于开发大型Web应用。Yii采用严格的OOP编写,并有着完善的库引用以及全面的教程。 废话不多说了,直接给大家贴代码了。 完整示例: 以上代码就是关于PHP Yii框架之表单验证规则大全的全部内容,希望大家喜欢。
我正在为网关后面的服务开发一种方法,以便于通信。 该计划使用oauth令牌中的作用域,该令牌将由运行在同一系统上的服务使用。 例如。如果oauth令牌具有作用域“acct”和“user”,bellow api方法将可用。如果ouath令牌的作用域为'admin',但缺少作用域'acct'和'user'中的一个或两个,则在不更改注释的情况下,我希望它也可用。 我希望这个'admin'范围被全球接受,