在DDD中将全局规则验证放在何处

我是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. 将存储库注入实体
  2. 将存储库注入工厂
  3. 在域服务上创建操作
  4. ???

并且每个选项都更加详细:

我可以查询实体构造函数/属性中的存储库。但是我认为在实体中引用存储库是一种难闻的气味。

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之间的依赖关系。

我可以将此验证逻辑放在UserFactory中。但是,如果我们想更改现有用户的名称怎么办?

我可以创建用于创建和编辑用户的域服务。但是有人可以直接编辑用户名而无需调用该服务…

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);

}

}

谢谢!

回答:

在大多数情况下,最好将此类规则放在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应用程序可能是你的兴趣。

以上是 在DDD中将全局规则验证放在何处 的全部内容, 来源链接: utcz.com/qa/425476.html

回到顶部