Money.java
package com.example.orderplatform;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Locale;
import java.util.Objects;
/**
* Monetary value object used by pricing, orders and payments.
*
* <p>Amounts are normalized to two decimal places and currencies are stored as uppercase ISO-4217
* codes. Negative amounts and mixed-currency arithmetic are rejected as business rule violations.
*
* @param amount decimal amount, rounded half up to currency-style precision
* @param currency three-letter currency code
*/
public record Money(BigDecimal amount, String currency) {
public Money {
Objects.requireNonNull(amount, "amount is required");
Objects.requireNonNull(currency, "currency is required");
currency = currency.strip();
if (!currency.matches("[A-Za-z]{3}")) {
throw new BusinessRuleViolationException("Currency must use the ISO-4217 three-letter code.");
}
if (amount.signum() < 0) {
throw new BusinessRuleViolationException("Money amount cannot be negative.");
}
amount = amount.setScale(2, RoundingMode.HALF_UP);
currency = currency.toUpperCase(Locale.ROOT);
}
/**
* Creates a normalized money value.
*
* @param amount decimal amount
* @param currency three-letter currency code
* @return normalized money value
*/
public static Money of(BigDecimal amount, String currency) {
return new Money(amount, currency);
}
/**
* Adds two monetary values after verifying they use the same currency.
*
* @param other value to add
* @return sum of both values
* @throws BusinessRuleViolationException when currencies differ
*/
public Money add(Money other) {
requireSameCurrency(other);
return new Money(amount.add(other.amount), currency);
}
/**
* Multiplies this value by a positive quantity.
*
* @param factor positive multiplier
* @return multiplied money value
* @throws BusinessRuleViolationException when the multiplier is less than one
*/
public Money multiply(int factor) {
if (factor < 1) {
throw new BusinessRuleViolationException("Quantity must be greater than zero.");
}
return new Money(amount.multiply(BigDecimal.valueOf(factor)), currency);
}
private void requireSameCurrency(Money other) {
Objects.requireNonNull(other, "other is required");
if (!currency.equals(other.currency)) {
throw new BusinessRuleViolationException("All monetary values must use the same currency.");
}
}
}