哪里就摆在DDD全球规则验证 [英] Where to put global rules validation in DDD

查看:231
本文介绍了哪里就摆在DDD全球规则验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新的国内长途,我试图在现实生活中应用它。有没有对这种验证逻辑的问题,如空检查,空字符串检查等 - 直接进入实体构造函数/属性。但是,在把验证像'唯一的用户名'一些全球性规则?

I'm new to DDD, and I'm trying to apply it in real life. There is no questions about such validation logic, as null check, empty strings check, etc - that goes directly to entity constructor/property. But where to put validation of some global rules like 'Unique user name'?

因此​​,我们有实体用户

So, we have entity User

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



选项包括:

Options are:


  1. 注入资源库实体

  2. 注入库出厂

  3. 域服务

  4. ???

  1. Inject repository to entity
  2. Inject repository to factory
  3. Create operation on domain service
  4. ???

和每个选项的更详细的:

And each option more detailed:

1 .Inject资源库实体

我可以在实体构造函数/属性查询库。但我认为,保持参考实体库是难闻的气味。

I can query repository in entities constructor/property. But I think that keeping reference to repository in entity is a bad smell.

public User(IUserRepository repository)
{
    _repository = repository;
}

public string Name
{
    get { return _name; }
    set 
    {
       if (_repository.FindByName(value) != null)
          throw new UserAlreadyExistsException();

       _name = value; 
    }
}



更​​新:我们可以用DI隐藏用户之间的依赖并通过IUserRepository规范对象。

Update: We can use DI to hide dependency between User and IUserRepository via Specification object.

2。库注入到出厂

我可以把这个验证逻辑UserFactory。但是,如果我们想改变已经存在的用户名?

I can put this verification logic in UserFactory. But what if we want to change name of already existing user?

3。创建域服务操作

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

I can create domain service for creating and editing users. But someone can directly edit name of user without calling that service...

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。 ???

你在哪里把全球的验证逻辑实体?

谢谢!

推荐答案

大多数时候,最好是将这些样的规则规格的对象。
你可以把这些规格在你的域包,所以使用域包谁有访问它们。使用说明书,你可以与你的实体捆绑你的业务规则,而无需创建与服务和存储库不需要的依赖性难以阅读的实体。如果需要,你可以注入的服务或资源库依赖关系的规范。

Most of the times it is best to place these kind of rules in Specification objects. You can place these Specifications in your domain packages, so anybody using your domain package has access to them. Using a specification, you can bundle your business rules with your entities, without creating difficult-to-read entities with undesired dependencies on services and repositories. If needed, you can inject dependencies on services or repositories into a specification.

根据上下文,可以使用规范的对象,构建不同的验证。

Depending on the context, you can build different validators using the specification objects.

实体的主要关注点应跟踪业务状态的 - 这足够责任,他们不应该与验证有关。

Main concern of entities should be keeping track of business state - that's enough of a responsibility and they shouldn't be concerned with validation.

示例

public class User
{
    public string Id { get; set; }
    public string Name { get; set; }
}



两种规格:

Two specifications:

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

注释

我觉得维杰 - 帕特尔早些时候的答案是在正确的方向,但我觉得这是一个有点过。他建议,用户实体取决于本说明书中,在那里我认为这应该是周围的其他方法。通过这种方式,可以让规范依赖于服务,资料库和环境一般,没有让你的实体依赖于它们通过一个规范的依赖关系。

I think Vijay Patel's earlier answer is in the right direction, but I feel it's a bit off. He suggests that the user entity depends on the specification, where I belief that this should be the other way around. This way, you can let the specification depend on services, repositories and context in general, without making your entity depend on them through a specification dependency.

参考

一个有关用例子很好地回答问题:的验证在领域驱动设计

A related question with a good answer with example: Validation in a Domain Driven Design.

Eric Evans的描述了用于验证,选择和对象的构造规范模式在第9章,第145

Eric Evans describes the use of the specification pattern for validation, selection and object construction in chapter 9, pp 145.

这的在规范模式的文章:在.NET应用程序的可能是你的兴趣。

This article on the specification pattern with an application in .Net might be of interest to you.

这篇关于哪里就摆在DDD全球规则验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆