Search code examples
springvalidationspring-mvcdesign-patternsspring-annotations

Validate when fields depend on one another


I am passing a request object name Person to controller. Lets say the object has 2 two fields. The following business rule apply:

  • If field age has a value < 18, the field sin should be left blank;
  • If not, it will produce exception with message the sin should be blank with age < 18 or another way is to set the field sin to empty string("").

What is the best way for me to validate those inputs when they depend on each other. My way to deal with them is to validate them inside the controller method. So it should look something like that

@GetMapping("/..."
public ResponseEntity<PersonResponse> getPersonResult(GetPersonRequest request)
{
    if (request.getAge() < 18)
    {
        if (request.getSin.length > 0)
            request.setSin("") 
    }
    PersonResponse response = callThirdPartyAPIToRetrieveInformationAboutThatPerson(request)
    return response ;
}

Is there any more elegant way to code ? Is it ok for the controller method to contain any validation logic like that ? am i violating the Single Responsibility in SOLID design ?


Solution

  • Yes, of course! And this is a good approach: single responsibility of classes - a controller is responsible for handling data, validator - for validation of data; open-closed principle - validated data is unchangeable by controller's method; Liskov principle correlates with the base OOP principles - a validator is separated entity and can be changed to another one without any additional manipulations; Interface Segregation is clear without any description (fully separated classes); Depency Inversion is also understandable - using annotation interface, controller does not know anything about its implementation. So, it's a really good approach from ideology and language syntax.

    Implementation.

    1. Create class-level @interface. All fields are accessible.
    2. Create ConstraintValidator class with validation logic.
    3. Set this annotation for @RequestBody in the controller method.
    4. Add validation functionality for controller: @Validated for controller class and @Valid for @RequestBody entity in controller method.

    If you need to handle validation exceptions, just throw a new exception and handle it in @ControllerAdvise class, no handling code in validation or controller classes.

    Example of creation class-level validator in the official resource.