验证具有不同状态的值对象 - 密码大小写



我正在设计库,它应该可以帮助我在未来项目中设计领域。我做了一些基本的价值对象,如电子邮件地址或电话号码。这些都很容易。当这样的值对象可能有不同的规则集时,它开始变得有问题,这些规则集说它是否有效。

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

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

所以响应的第一部分是 - 我认为 - 密码应该只做基本的健全性检查,就像isNullOrEmptyString一样,仅此而已。但是我什么时候应该做适当的验证?在实体创建期间?在服务层的某个地方持久化之前?实体想法对我来说听起来还不错,但setPassword(Password password怎么知道我想使用的策略?我尽量避免在我的项目中出现单例,几乎所有事情都是通过 DI 完成的。

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

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

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

我正在考虑将战略帕滕作为这里的解决方案。但是以这种方式构建这样的VO:新的密码("123456",新的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(密码密码)如何知道我想使用的策略?

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

因为如果你仔细观察需求,你可能会发现 setPassword() 方法并不存在于明显的地方。

如果将密码建模为实体的属性,则实体还需要了解密码策略,以及如何知道? 更改密码策略是否需要更改每个实体?您可以有两个具有不同密码策略的实体等吗?

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

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

您可以使用构建器模式创建复杂的对象。

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

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

你的问题听起来不像是你想走DDD的方式,但你用DDD标签标记了你的问题 - 所以我假设你想要一个DDD答案:-)。

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

在哪里放置值对象验证

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

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

使验证逻辑可重用

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

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

对于具有不同密码策略的特定情况,您可以只实现一堆不同的规范,每个策略一个规范。你可以组合更简单的来制作更复杂的。

相关内容

  • 没有找到相关文章

最新更新