Java – In which layer should validation be located

javaspringvalidation

I'm creating a Rest API using Spring Boot and I am using Hibernate Validation to validate request inputs.

But I also need other kinds of validation, for example when update data needs to checked, if the company id doesn't exist I want to throw a custom exception.

Should this validation be located at the service layer or the controller layer?

Service Layer :

 public Company update(Company entity) {
    if (entity.getId() == null || repository.findOne(entity.getId()) == null) {
        throw new ResourceNotFoundException("can not update un existence data with id : " 
            + entity.getId());
    }
    return repository.saveAndFlush(entity);
}

Controller Layer :

public HttpEntity<CompanyResource> update(@Valid @RequestBody Company companyRequest) {
    Company company = companyService.getById(companyRequest.getId());
    Precondition.checkDataFound(company, 
        "Can't not find data with id : " + companyRequest.getId());

    // TODO : extract ignore properties to constant

    BeanUtils.copyProperties(companyRequest, company, "createdBy", "createdDate",
            "updatedBy", "updatedDate", "version", "markForDelete");
    Company updatedCompany = companyService.update(company);
    CompanyResource companyResource = companyAssembler.toResource(updatedCompany);
    return new ResponseEntity<CompanyResource>(companyResource, HttpStatus.OK);
}

Best Answer

Both controller layer and service layer expose certain interfaces. Interfaces define contracts on how the interface should be used. Contract usually means which arguments (and its types and values) are expected, which exceptions can be thrown, which side effects are created etc.

Now, your validation is essentially enforcement of the contract of controller update() method and service layer update() method. Both of them have very similar contract so it would be natural if the validation (enforcement of the contract) would be common too.

One possible way to do that is to separate the validation of this contract and have it called in both layers. This is usually most clear - each class/method enforces their own contract, but is often unpractical because of performance (accessing database) or other reasons.

Other possibility is to delegate this validation to the service layer while explicitly defining the behavior in case of failed validation in the service layer contract. Service layer will typically return some generic validation error (or throw exception) and controller layer will want to react in some specific way to the error - in this case we will return 400 Bad request to signal, that incoming request was invalid.

In this design, there's a danger of too much coupling between business logic in service layer (which should be quite generic) and controller (which handles integration logic).

Anyway, this is quite controversial question and 100 people will answer with 100 answers. This is just my take on it.

Related Topic