【问题标题】:Validation of Value Object with different stategies - password case验证不同状态的值对象 - 密码案例
【发布时间】:2016-07-03 02:45:10
【问题描述】:

我正在设计库,它应该可以帮助我在未来的项目中设计域。我制作了一些基本的值对象,例如 EmailAddress 或 PhoneNumber。这些很容易。当这样的值对象可能有不同的规则集来说明它是否有效时,它开始对我产生问题。

让我们在这里以Password 为例,因为这是我写这个问题的真正原因。在以前的项目中,我在Password construtor 中验证了密码规则集。但它使这个实现项目具体化,并且违反了 - 我认为 - 关注分离原则。

我认为 Strategy Patten 是这里的解决方案。但是以这种方式构建这样的 VO:new Password("123456", new AwfullyLoosePasswordValidationStrategy()) 对我来说听起来很糟糕。我也在考虑 VO setValidationStrategy(PasswordValidationStrategy strategy) 中的静态设置器,但这仍然违反关注点分离,对我来说听起来很奇怪。

所以响应的第一部分是——我认为——密码应该只做基本的健全性检查,比如isNullOrEmptyString,仅此而已。但是我什么时候应该进行适当的验证?在实体创建期间?在坚持服务层的某个地方之前?实体想法对我来说听起来并没有那么糟糕,但是setPassword(Password password) 如何知道我想使用的策略?我尽量避免在我的项目中使用单例,几乎所有事情都是通过 DI 完成的。

tl;dr:我应该在哪里验证无法在其构造函数中验证的值对象?

【问题讨论】:

  • 好吧,我建议您在创建实体之前进行验证。不创建包含错误内容的内容更容易(相对于允许创建它;然后处理您现在必须处理的特殊情况)。您也可以考虑验证两次(例如直接在某些网页中进行验证 - 您可以在此处知道所提供的密码无效;当然这会导致代码重复)。

标签: java validation domain-driven-design value-objects


【解决方案1】:

我应该在哪里验证无法在其构造函数中验证的值对象?

我不相信你有这样的东西。您永远不应该创建无效的值对象。构造函数(如果您愿意,可以替代工厂或工厂方法)验证参数,如果它们是可接受的,则创建一个格式良好、不可变的值对象,然后您就完成了。

我认为 Strategy Patten 是这里的解决方案。但是以这种方式构建这样的 VO:new Password("123456", new AwfullyLoosePasswordValidationStrategy()) 对我来说听起来很糟糕。

我不知道策略模式在值对象中有意义的任何情况。如果值类型的查询需要策略,您似乎更有可能将策略作为参数传递给查询,而不是使其固有在类型上。

也就是说,在我看来,您在这里已经非常接近正确的想法了;你只是倒退了

PasswordValidator validator = new AwfullyLoosePasswordValidator();
Password password = validator.createPassword("123456");

也就是说,工厂根据您的策略验证输入,如果可以接受,则将该输入传递给 Password 构造函数(它也执行自己的检查)。

或者,您可以将密码策略实施为实体创建的一部分,而不是密码创建的一部分

class EntityFactory {
    private final PassswordValidator passwordValidator;

    Entity create(Password password) {
        passwordValidator.check(password);
        return new Entity(password);
    }

实体想法对我来说听起来并没有那么糟糕,但是 setPassword(Password password) 如何知道我想使用的策略?

现在是一个非常重要的问题,你应该非常仔细地研究。

因为如果您仔细查看需求,您可能会发现 setPassword() 方法并不存在于明显的位置。

如果您将密码建模为实体的属性,那么实体还需要了解密码策略,它应该如何知道?更改密码策略是否需要更改每个实体?您能否拥有两个具有不同密码策略等的实体?

或者,您的系统中可能有一个拥有密码策略的聚合。在这种情况下,该聚合也可能负责所有密码(无论如何都在该策略的范围内)。也就是说,密码不是实体的属性,而是实际上可能是字典中的一个值,您可以在其中使用 entityId 进行查找。

的重点是用您的通用语言挖掘并与您的领域专家一起审查需求。不要羞于确保您对自己的领域有透彻的了解。

【讨论】:

    【解决方案2】:

    您的问题听起来不像是在尝试采用 DDD 方式,但您使用 DDD 标签标记了您的问题 - 所以我假设您想要一个 DDD 答案:-)。 em>

    首先,不要在 VO 中进行复杂的验证。这将使它们难以使用。 VO 本质上是一个简单的概念,因此请尽量保持这种方式。尤其是添加对服务的引用(如您建议的策略)通常是一个坏主意。

    值对象验证的放置位置

    因此,您应该只验证最基本的内容,例如 VO 中的空检查。恕我直言,正则表达式检查已经太多了,但我相信对此有不同的看法。

    现在您在 VO 中没有验证逻辑,您需要准备好查看无效的 VO。 您的域模型应在 VO 进入域后立即对其进行验证,例如在以VO为参数的实体方法中。

    使验证逻辑可重用

    如果您将 VO 验证逻辑放在实体方法中,您很快就会遇到想要从另一个实体方法验证相同 VO 的情况。 处理此问题的最佳方法是规范模式。

    This answer 提供模式的描述。规范的好处是它们是可重用和可组合的,例如从一些较小的规格中制作出更大的规格。此外,他们倾向于记录验证对于特定 VO 是如何工作的,并且他们可以依赖于服务。

    对于您使用不同密码策略的特定情况,您可以实施一系列不同的规范,每个策略一个规范。您可以将更简单的组合起来制作更复杂的。

    【讨论】:

    • 也许听起来不像,但这就是我想要实现的!可能我也无法向谷歌表达它:)
    【解决方案3】:

    您可以使用Builder pattern 来创建复杂的对象。

    公共类密码{ 私有字符串密码;

    private Password(String password){
    
    }
    
    
    public static class Builder{
        private String password;
        private Validation validation;
    
        public Builder(){
        }
    
        public Builder setPassword(String password){
            this.password = password;
        }
    
        public Builder setValidation(Validation validation){
            this.validation = validation;
        }
    
        public Password build(){
            if (null == validation){
                 return null;
            }
            if (validation.validate(password)){
                 return new Password(password);
            }
            else{
                 return null;
            }
        }
    }
    }
    

    然后您可以将其用作

    Password password = new Password.Builder().setPassword("123456")
                                              .setValidation( new AwfullyLoosePasswordValidationStrategy())
                                              .build();
    

    【讨论】:

      猜你喜欢
      • 2018-05-19
      • 2014-12-20
      • 1970-01-01
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      • 2021-06-02
      • 1970-01-01
      相关资源
      最近更新 更多