PricingApplicationService.java
package com.example.orderplatform.pricing.application;
import com.example.orderplatform.BusinessRuleViolationException;
import com.example.orderplatform.Money;
import com.example.orderplatform.ResourceNotFoundException;
import com.example.orderplatform.pricing.api.PricedLine;
import com.example.orderplatform.pricing.api.PricingQuote;
import com.example.orderplatform.pricing.api.PricingRequest;
import com.example.orderplatform.pricing.api.PricingService;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Application service that reads the active catalog and calculates quote totals for order placement.
*/
@Service
@Transactional(readOnly = true)
public class PricingApplicationService implements PricingService {
private final PriceCatalog catalog;
public PricingApplicationService(PriceCatalog catalog) {
this.catalog = catalog;
}
/**
* Quotes the requested products using active catalog entries.
*
* @param requests product and quantity requests
* @return priced quote with one line per request
* @throws BusinessRuleViolationException when the request is empty, has an invalid quantity or mixes currencies
* @throws ResourceNotFoundException when a product code is not active in the catalog
*/
@Override
public PricingQuote quoteFor(List<PricingRequest> requests) {
if (requests == null || requests.isEmpty()) {
throw new BusinessRuleViolationException("At least one order item is required.");
}
List<PricedLine> lines = requests.stream()
.map(this::price)
.toList();
Money total = lines.stream()
.map(PricedLine::lineTotal)
.reduce((left, right) -> left.add(right))
.orElseThrow();
return new PricingQuote(lines, total);
}
private PricedLine price(PricingRequest request) {
if (request.quantity() < 1) {
throw new BusinessRuleViolationException("Quantity must be greater than zero.");
}
CatalogPrice item = catalog.findActivePrice(request.productCode())
.orElseThrow(() -> new ResourceNotFoundException("Product " + request.productCode() + " was not found."));
return new PricedLine(
item.productCode(),
request.quantity(),
item.unitPrice(),
item.unitPrice().multiply(request.quantity()));
}
}