Search code examples
javavalidationspring-mvcmodel-view-controller

MVC design - Can I have domain model validation in the Controller class?


The following is just an exmaple for context not the actual implementation. Using Spring MVC, I have the following Model which has validation based on annotations.

@Entity
public class Customer {
   @Id private int id;
   @NotNull private String name;
}

And the following DTO used to map the data received in the request's body in the Controller's createNewCustomer function.

public class CustmerDTO{
    private String name;
}

In my Controller I am using the modelMapper to convert the customerDTO to a new domain model Customer object. Based on the @NotNull annotation, if the name property of the recieved object (customerDTO) is empty the ConstraintViolationException is thrown.

public class CustomerController {
    @Autowired private CustomerService customerService;
    @Autowired private ModelMapper modelMapper;

    @PostMapping(value = "/customer")
    public Customer createNewCustomer (@RequestBody CustomerDTO customerDTO) {
        try {
            Customer newCustomer = modelMapper.map(customerDTO, Customer.class);
            return customerService.saveCustomer(newCustomer);
        }
        catch (ConstraintViolationException e) {
            throw new CustomerMissingInformation();
        }
    }
}

As you can see here I handle validation for the Customer in the Controller, which by definition is not a good habit, as Controllers in MVC are part of the presentation layer, and should have no clue about how to perform data validation, also I want to keep my controllers as light as possible.

Can I keep this design or is there any good approach to move my validation in the Service layer while keeping validation annotations and also allowing the Controller to be able to receive representation objects(DTOs) and convert them to domain models?


Solution

  • Looking more into this, I came to the following conclusion. Validation for persisting an object should happen as soon as possible and at property level, as you don't want your persistence layer or any mappers that you might be using deal with null when it is not expected. Everything that goes past property validation, shall be approached from a contextual validation perspective, and be passed to methods that hold logic for a given context.

    Is this order valid to be filled, is this customer valid to check in to the hotel. So rather than have methods like isValid have methods like isValidForCheckIn.