在设置每个属性时验证域实体,或者稍后一次验证所有属性


Validate a domain entity upon setting each property, or all at once later?

验证域实体时,是按设置验证值更好,还是稍后使用验证器(如Symfony2)一次性验证值更好?

例如:

选项1。设置时验证

public function setEmail($email)
{
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new EntityException('The specified email address ' . $email . ' is invalid.');
    }
    $this->_email = $email;
    return $this;
}

选项2。稍后验证

$user = new UserEntity();
$user->setEmail('johnnie@duh.com');
$validator = new Validator();
$validator->validate($user);

选项3。以上两项(尽管看起来有点多余,不值得开销)

我知道第一个可能会使实体更加严密,但第二个可能会让错误处理更加用户友好。

此外,第二个选项似乎更容易配置和维护。。。因为我不必调整setter,而且我可以将验证逻辑集中到一个类中。

归根结底…
因此,基本上,这听起来像是选项2是我想要做的,但我觉得牺牲实体的气密性(例如,如果我忘记通过验证器运行实体)可能是愚蠢的。

毫无疑问,在某些情况下,应用程序级别的验证是不必要的(即,您知道在某个特定点使用的是有效数据)。尽管在这些情况下应用验证没有错,但它肯定是多余的。

此外,您可能会发现自己处于这样一种场景中,您希望对传入数据执行"大规模验证"。这可能更多地应用于服务或自定义实现的表单,而您没有这些表单,例如Symfony的表单验证框架。

这些倾向于表明,选项2是更可取的方法,可能需要注意的是,当试图持久化对象时,验证器应该自动调用(例如,在模型的预保存事件期间;我还不熟悉原则2,但原则1.2确实有自己的验证框架可以实现这一点)。

拥有无效数据肯定不是一种理想的情况,但只要你能确信无效数据不会被持久化,这就让你可以自由地相信你从数据库中提取的任何数据都是有效的(注意,我在这里说的是"有效的",而不是"值得信赖的"!)。

单一责任原则

最好的方法是在一个单独的层中进行所有必要的验证。这样将更容易维护和测试验证器。也更容易在应用程序中验证数据。

不要重复自己

您不必为每个实体调用validate()。

您所要做的就是在存储库层或服务层(如果您有)上实现验证。

$user = new User();
// [...]
$user->setEmail('myinvalidemail#blah,com');
$repository->save($user);

因此,在您的用户存储库中

UserRepository extends AbstractRepository {}

以及所有实体的通用验证:

abstract class AbstractRepository {
    public function save($entity) {
        $validator = // Get your validator based on the entity's name or something else
        $validator->validate($entity); // Throws exceptions or flag the  fields for future use
         // Now save it...
    }
}