Issue
I have client code that calls a web service to get monthly prices within a date range and then returns the average annual price:
@Service
@RequiredArgsConstructor
@Slf4j
public class PriceAverageService {
private final PriceServiceClient priceServiceClient ;
public BigDecimal getAverageAnnualPrice(Long productId, LocalDate startDate, LocalDate endDate) {
List<Price> prices = priceServiceClient.getMonthlyPrices(productId, startDate, endDate);
// code to compute average annual price
return averageMonthlyPrice;
}
}
@FeignClient(name="price-service")
@Validated
public interface PriceServiceClient {
@GetMapping("/product/price")
@Valid
@ConsecutivePrices
List<Price> getMonthlyPrices(
@RequestParam(name="productId")
Long productId,
@RequestParam(name="startDate")
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)
LocalDate startDate,
@RequestParam(name="endDate")
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)
LocalDate endDate);
}
Price
class:
@Value
public class Price {
BigDecimal price;
LocalDate priceDate;
}
It is an error if a price is missing for a month where adjacent months are not missing a price. For example, the following price data is an error: $15.00 Jan 2020, $20.00 Mar 2020. That's because Feb 2020 is missing. @ConsecutivePrices
is a ConstraintValidator
that checks for this condition.
I also need to check that there is a price for the startDate
and the endDate
. It is not an error if either is missing. If either is missing, the code should return null for getAverageAnnualPrice
(instead of computing the average annual price).
Should a ConstraintValidator
be used to check that the startDate
and endDate
prices exist? If so, would I just catch MethodArgumentNotValidException
inside of getAverageAnnualPrice
?
Solution
I would not use a ConstraintValidator for implementing a part of the domain logic of your service. As you said, the missing start/end date prices are not an error but an expected case that should be handled gracefully by your service. (Generally, I would try to avoid the use of exceptions for normal/happy cases. This includes the use of ConstraintValidators.)
In contrast, a missing consecutive price does not have sense in your domain model (according to your explanation), i.e. the invoked service should never return this unless something really unexpected has happened (e.g. corrupted data model). Hence in this case a validator (and throwing an exception) could be appropriate. If that condition is not met (i.e. if it could be normal that a missing consecutive price is returned sometimes, e.g. because the product was temporarily out of sale or something like that), then I would reconsider the use of a ConstraintValidator even in that case.
(Disclaimer: I'm not a Spring user, and as a Java user I have never used constraint validators in client interfaces, so I could be missing something. However I think these considerations are rather generic.)
Answered By - jsiwrk