Validate form input in Domain Objects setters?

2019-06-24 05:32发布

问题:

Since I got into learning about MVC I have always validated my form data in my controllers which is a habit I picked up while skimming through CodeIgniters code but I have learned that its way of doing certain operations is not the best, it just gets the job done.

Should all form data be validated by the domain objects? And if so should it be done in the setters like so

public function setFirstName($firstName) {

    // Check if the field was required
    if(!$firstName) {
        throw new InvalidArgumentException('The "First name" field is required');
    }

    // Check the length of the data
    // Check the format 
    // Etc etc
}

Also, for example, if I am handling a basic user registration my User class does not have a $confirmPassword property so I would not be doing

$user->setConfirmPassword($confirmPassword);.

One way of checking if the two passwords entered are equal would be to set the $password and do something like

$user->setPassword($password);
if(!$user->matchPassword($confirmPassword)) {
    throw new PasswordsNotEqualException('Some message');
}

and this would be done in the Service layer I would think?

Any advice to help me in the correct direction would be great. Thanks.

回答1:

Should all form data be validated by the domain objects? And if so should it be done in the setters like so

IMO you should only let creation of valid objects, and the best way to archieve this is to make those checks in the method that creates an object.

Assuming that the first name of an user cannot be changed, you would validate that upon the user creation. This way, you forget about the setter because you won't need it anymore.

There may be cases when you want a property to be changed, and you would need to validate them too (because that change could lead form a valid object to an invalid object, if that's the case).

One way of checking if the two passwords entered are equal would be to set the $password and do something like...

You can handle this one the same way: have a Password object that checks both the password and confirmation upon its creation. One you have a valid Password instance, you can use it knowing that it passed all the validations you specified.

References

Those design principles (complete and valid objects from the start, etc) are from Hernan Wilkinson's "Design Principles Behind Patagonia".Be sure to check the ESUG 2010 Video and the presentation slides.

I've recently answered another question about validating properties I think you may come in handy: https://stackoverflow.com/a/14867390/146124

Cheers!



回答2:

TL;DR

No, setters should not be validating the data. And nick2083 is completely wrong.

Longer version ...

According to Tim Howard's provided definition [source], the domain objects can verify the state of domain information that they contain. This basically states, that for you to actually have a domain object, said object need to be able to validate itself.

When to validate

You basically have to options:

  • validate in each setter
  • have one method to validate whole object

If the validation is a part of setter, there is one major drawback: the order of setters matters.

Example: lets say you are making an application which deals with life insurance. It is quite probable, that you will have a domain object which contains the person that is insured and the person that gets awarded the premium, when policy is triggered (the insured on dies). You would have to make sure that the recipient and the insured are not the same person. But there is no rule to govern in which order you execute the setters.

When you have two or more parameters in domain object, which have to be validated against each-other, the implementation becomes a bit fuzzy. The most feasible solution is to check when all parameters are assigned, but at that point you have already lost the benefit of in-setter validation: the execution of code has moved past the origin of invalid data.

And how would you deal with situations, where the valid state of domain object is not having a parameter A set if parameter B is large then 21 and C is already set?

Conclusion: validation in setters is only the viable solution, when you have very simple domain objects, with no tangled validation rules.