Object validation

A while back I was talking with a colleague about domain object validation for an application built using the Spring framework. My bioinformatics team at the CFG has always put the validation logic into the business object itself. In the case of command objects (the objects that hold form state), this means we create a custom interface, Validatable, that declares a single method, void validate(Errors errors) (the Errors interface is part the Spring validation machinery, representing a smart hash of errors by object property). Calling this method records any validation issues into the Errors object passed to validate(). We also need a simple Validator called ValidatableValidator that takes a Validatable and calls validate(errors) on it.

This always seemed the right approach to me, because after all in object-oriented design you should always move operations that act on data close to the data themselves, i.e. put the validate method that checks for valid data state in the object that encapsulates that data, i.e. tell, don’t ask.

My colleague was of the opposite opinion. He felt that command objects should just be dumb objects (“objects” with only data and accessor methods, no business logic), and all validation logic should be kept in separate validator objects. When I protested that dumb objects were an anathema in object-oriented code, he asked me why Spring provided a Validator interface and not a Validatable interface. I had to admit I was a little dumbfounded. I generally think Spring is well-written, but it did seem they were explicitly recommending splitting validation behavior out of the object being validated.

Thinking about it some more, I’ve decided that while I still generally think that validation logic should stay in the object being validated, there are cases when you’d want to split it out.

  • when the validated object’s class already has lots of behavior, is getting too big and needs to be split up into a few collaborating classes, closely coupled to one another but not to other classes
  • when the same validation logic occurs in multiple business objects (e.g. this string is not null, empty or whitespace) and you’d like to simplify maintenance and keep that logic in only one place, though this can just as easily be done using a static method, e.g. StringUtils.isBlank(text)
  • when validation depends on capabilities foreign to the object itself (e.g. checking uniqueness of a username via a data access object (DAO) — it often makes good sense to split the responsibility for data access out of domain objects and into DAOs)

Another example of the last case is anything that has to interact with the network. You don’t want your domain objects depending on a network, especially if you want unit tests to run rapidly and reliably without access to a network (e.g. when you’re sitting in an airport that charges $10/hour for wireless internet access). In the neuromice.org code, when we validate mouse data pulled in from one of the member sites, we check all links provided to make sure they work and refer to the expected content, and we do this using an external validator class. However, we avoid violating the “tell, don’t ask” principle by using something similar to the visitor pattern. The object being validated has an accept(ValidationVisitor validator) method. The validator passed in has methods like validateLink(String url), etc. In our unit tests we pass in either a mock or a stub ValidationVisitor that does not actually hit the network. This allows us to separate out responsibilities nicely without requiring brittle procedural code sitting in an unrelated class like

class SomeFormValidator implements Validator {
  public void validate(Object object, Errors errors) {
    // oddly the cast is still necessary in Spring 2.0 M3
    MutantLine line = (MutantLine) object; 
    if (!LinkValidator.isValidLink(line.getGeneLink()) {
      errors.addError("etc. etc. etc."); 
    }
    ...
  }
}

Instead the code is something like

class MutantLine { ...
  public void accept(ValidationVisitor validator) {
    validator.validateLink(this.geneLink, "gene link");
    associatedObject.accept(validator);
    ...
  }
}

Note that the ValidationVisitor encapsulates the Errors object.

As an interesting aside, in Ruby on Rails, validation is baked into domain objects, including validation that depends on data access. This is because Rails’ ActiveRecord, unsurprisingly, follows the Active Record pattern, in which the object is both a domain object and a DAO.

This entry was posted in Software. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">