Some weeks ago I wrote a blog about Pros and cons of service layer designs. While that blog addresses the service layer design with pure java, this blog is about a service layer design using java bean validation as defined by jsr-303. Both strategies that I have discussed in the Pros and cons of service layer designs blog can be implemented easier by using the java bean validation framework.
Let’s take a look at the shared request and response type strategy. The problem of this strategy is that the request and response types are used in a lot of different service methods and therefore they must define the properties of all service methods. But when calling a specific service method the request objects must be in a certain state (the service method’s pre condition).
A placeOrder() service method might need an OrderTO that has a list of orderItems and a customerNumber. The OrderTO also defines a placedDate and a orderNumber property, because it is also used as the return type of the service method. These properties must be null when the placeOrder() method gets invoked. The service method’s pre-condition can be defined by using jsr-303 constraints as the next example shows.
public interface OrderService { public OrderTO placeOrder(@Valid OrderTO orderToPlace); } public class OrderTO { @NotNull @Size(min=1) private List<ItemTO> orderItems; @Min(0) private int customerNumber; @Null private Date placedDate; @Null private Integer orderNumber; public OrderTO(){; } // ... getter and setter methods }
I will now explain in detail what the constraint definitions mean.
The OrderService defines one argument and it is annotated with the jsr-303 @Valid annotation. The @Valid annotation means that the argument object must be valid. In terms of the java bean validation an object is valid if all it’s constraints are valid. So that annotation triggers the validation of the OrderTO‘s property constraints.
The constraint definition above has one problem. All constraints only describe the pre-conditions of the placeOrder() method. The placeOrder() method also returns an OrderTO and of course that OrderTO must be in a different state. Therefore other constraints must be validated. The returned OrderTO must have a placedDate and an orderNumber, but these properties have @Null constraints. While that constraints are appropriated for the pre-condition they are inaceptable for the post-condition.Therefore we need different constraints that validate the post-condition of the service method.
But how can we define constraints that are contrary to each other? Fortunately jsr-303 gives an answer.
Each constraint can define groups that it belongs to and a validator can be asked to only validate an object against specific groups.
Unfortuanetly these groups must be classes and can not be simple strings. So we have to define a group (class or interface) for every pre- and post-condition or for every state we want to validate.
For our case above we can introduce a PlaceOrderRequest and a PlaceOrderResponse interface to reflect these different validation groups.
The code will change to:
public interface PlaceOrderRequest {} public interface PlaceOrderResponse {} public class OrderTO { @NotNull(groups={PlaceOrderRequest.class, PlaceOrderResponse.class}) @Size(min=1,groups={PlaceOrderRequest.class, PlaceOrderResponse.class}) private List<ItemTO> orderItems; @Min(value=0, groups={PlaceOrderRequest.class, PlaceOrderResponse.class}) private int customerNumber; @Null(groups={PlaceOrderRequest.class}) @NotNull(groups={PlaceOrderResponse.class}) private Date placedDate; @Null(groups={PlaceOrderRequest.class}) @NotNull(groups={PlaceOrderResponse.class}) @Min(value=0,groups={PlaceOrderResponse.class}) private Integer orderNumber; public OrderTO(){; } // ... getter and setter methods }
Now that we have introduced different groups the service method implementation can validate it’s pre- and post-conditions by simply calling the jsr-303 validator and ask it to validate a specific group.
public class OrderServiceImpl implements OrderService { public OrderTO placeOrder(OrderTO placeOrderTO){ ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Set<ConstraintViolation<OrderTO>> requestViolations = validator.validate(placeOrderTO, PlaceOrderRequest.class); boolean requestHasViolations = !requestViolations.isEmpty(); if(requestHasViolations ){ // ... handle constraint violations } // ... do business logic OrderTO placedOrderTO = ... ; Set<ConstraintViolation<OrderTO>> responseViolations = validator.validate(placedOrderTO, PlaceOrderResponse.class); boolean responseHasViolations = !responseViolations.isEmpty(); if(responseHasViolations){ // ... handle constraint violations } } }
This example shows how the java bean validation framework can be used to ensure service api’s constraints. If you think about the code above you might have recognized that the pre and post validation logic might be the same for every service method. So it would be better to implement the validation using AOP.
If you want to get deeper into java bean validation take a look at spring 3 validation and the reference implementation hibernate validator.