15

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);
}
6

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.

1

Input should be checked in service layer.

And "Can't find id" is logical error condition. So should be thrown from controller layer.

This again depends on your layering / design.
What a service layer is supposed to do and what is expected from controller layer.

  • An answer shouldn't be seeking additional clarification from the question. If the question needs clarification it should commented on and possibly flagged for closure if it is too unclear. Yes, I do realize you don't have the reputation for either of those actions. – user40980 Feb 2 '16 at 14:04
0

In our Java shop, we have intentionally split web widget validation onto three separate operations.

  1. Basic formatting - numbers must be numbers; dates must be valid dates etc. Usually this validation comes for free - the web framework will do it for you when binding widget contents to the model.
  2. Single widget validation - date must be in the past; an integer must be between 1 and 100; customerId must exist in the database etc. This belongs in the controller layer in most cases, but may need support from the data repository.
  3. Cross-widget validation - checkout date must be after check-in date; date of death cannot be before date of birth etc. This is definitely business rule validation. We tend to put this in the controller layer as well, but you may want to shift it into a business validator so it can be reused.

If layer 1 fails, we don't check 2 or 3. Similarly if 1 succeeds and 2 fails we don't do 3. This stops spurious error messages being generated.

You are asking about values in a REST call rather than widget contents, but the same principles apply.

0

Hibernate validations are checks over the integrity of the data. In order to avoid RuntimeExceptions from bbdd. They are pretty much the same validations you should control with Constrains. Because only the business layer should be feeding the persistence layer, you may (or not, up to you) trust the rightness of the data that comes from your business layer

I don't put validations in DAOs. I expect valid data from upper layers. In case of an error I delegate to the bbdd the responsibility to be aware of its contents.

Then comes validations at the business layer. All business validations focused on keeping coherence of the data, not its integrity.

Finally I do previous validations on the control layer. Those related only with such layer.

You will see soon which validations are intended to be implented at business layer. The most common: id control. This one can easily be implemented at both layers. If you expect to have many controllers or clients consuming your business layer then instead of repeating the same validation everywhere, it will be an excelent candidate to be put at the business layer.

Sometimes controllers have their own rules and conditions that wont be reproduced in any other facade. Then it is a candidate to be put in such a controller.

Think about what you are validating for and if you wish to apply it for everyone no matter what. Or if it's a contextual validation ("Im validating something that only happens at a particular control / view facade).

-1

Test driven aproach shade a light on this,, after all there is no controller and you must choose another option. Obviously bussines rules should be in one place, and this is another constraint in your decisssion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.