Skip to main content

Overview

The Quantity module provides type-safe representations of measurable values with units. It includes support for physical quantities (weight, volume, etc.) and monetary values with multi-currency support.

Core Concepts

Quantity

Represents an amount with a unit of measurement:
Quantity Definition
public record Quantity(BigDecimal amount, Unit unit) 
    implements Comparable<Quantity> {
    
    public static Quantity of(BigDecimal amount, Unit unit);
    public static Quantity of(double amount, Unit unit);
    public static Quantity of(int amount, Unit unit);
    
    public Quantity add(Quantity other);
    public Quantity subtract(Quantity other);
}
Examples:
// Create quantities
Quantity weight = Quantity.of(100, Unit.of("kg", "kilogram"));
Quantity volume = Quantity.of(500, Unit.of("L", "liter"));
Quantity count = Quantity.of(1000, Unit.pieces());

// Arithmetic operations (same units only)
Quantity total = Quantity.of(50, Unit.of("kg"))
    .add(Quantity.of(25, Unit.of("kg")));
// Result: 75 kg
Quantities with different units cannot be added or subtracted:
// This throws IllegalArgumentException
Quantity.of(100, Unit.of("kg"))
    .add(Quantity.of(50, Unit.of("L")));

Unit

Represents a unit of measurement:
public record Unit(String code, String name) {
    public static Unit of(String code, String name);
    public static Unit pieces();
}
Common units:
Unit.of("kg", "kilogram")
Unit.of("L", "liter")
Unit.of("m", "meter")
Unit.of("m²", "square meter")
Unit.pieces()  // For countable items

Money Type

The Money class provides comprehensive support for monetary values with multiple currencies.

Creating Money

// Various ways to create PLN amounts
Money pln1 = Money.pln(100);              // from int
Money pln2 = Money.pln(99.99);            // from double
Money pln3 = Money.pln(new BigDecimal("123.45"));
Money pln4 = Money.pln("50.75");          // from string

// Zero values
Money zero = Money.zeroPln();
Money one = Money.onePln();

Arithmetic Operations

Money Operations
Money base = Money.pln(100);

// Addition and subtraction
Money sum = base.add(Money.pln(50));          // 150 PLN
Money diff = base.subtract(Money.pln(30));    // 70 PLN

// Multiplication
Money doubled = base.multiply(new BigDecimal("2"));      // 200 PLN
Money tripled = base.multiply(3);                        // 300 PLN

// Division
Money half = base.divide(new BigDecimal("2"));           // 50 PLN
Money divided = base.divide(new BigDecimal("3"), 
                           RoundingMode.HALF_UP);       // 33.33 PLN

// Division with remainder
Money[] result = base.divideAndRemainder(new BigDecimal("3"));
// result[0] = 33 PLN (quotient)
// result[1] = 1 PLN (remainder)

// Negation and absolute value
Money negative = base.negate();               // -100 PLN
Money abs = negative.abs();                   // 100 PLN

Percentage Calculations

Working with Percentages
Money amount = Money.pln(1000);

// Calculate percentage
Money discount = amount.multiply(Percentage.of(20));
// Result: 200 PLN (20% of 1000)

// Apply discount
Money final = amount.subtract(discount);
// Result: 800 PLN

// Common percentages
Money half = amount.multiply(Percentage.of(50));          // 500 PLN
Money same = amount.multiply(Percentage.oneHundred());    // 1000 PLN
Money none = amount.multiply(Percentage.zero());          // 0 PLN

Comparisons

Money m1 = Money.pln(100);
Money m2 = Money.pln(50);

// Direct comparisons
boolean greater = m1.isGreaterThan(m2);              // true
boolean greaterOrEq = m1.isGreaterThanOrEqualTo(m2); // true
boolean zero = m1.isZero();                          // false
boolean negative = m1.isNegative();                  // false

// Comparable interface
int comparison = m1.compareTo(m2);                   // > 0

Currency Handling

Currency Information
Money amount = Money.eur(150);

// Get currency code
String code = amount.currency();           // "EUR"

// Get currency unit (javax.money)
CurrencyUnit unit = amount.currencyUnit();

// Extract value
BigDecimal value = amount.value();         // 150

// String representation
String str = amount.toString();            // "EUR 150"
The Money class uses JavaMoney (JSR 354) internally via the javamoney-moneta library, ensuring proper currency handling and precision.

Real-World Usage

In Pricing Module

Pricing Calculation
Calculator calculator = new SimpleFixedCalculator(
    "basic-fee", 
    Money.pln(100)
);

Money price = calculator.calculate(parameters);

In Accounting Module

Account Transactions
Result<String, TransactionId> result = accountingFacade.transfer(
    fromAccount,
    toAccount,
    Money.pln(500),
    occurredAt,
    appliesAt
);

In Ordering Module

Order Pricing
OrderLine line = new OrderLine(
    lineId,
    productId,
    Quantity.of(5, Unit.pieces()),
    specification,
    OrderLinePricing.of(Money.pln(100), Money.pln(500))
);

Testing Examples

From MoneyTest.java:
Money Tests
@Test
void shouldAddTwoMoneyAmounts() {
    Money first = Money.pln(100);
    Money second = Money.pln(50);
    
    Money result = first.add(second);
    
    assertEquals(Money.pln(150), result);
}

@Test
void shouldMultiplyMoneyByPercentage() {
    Money money = Money.pln(1000);
    Percentage percentage = Percentage.of(20);
    
    Money result = money.multiply(percentage);
    
    // 1000 * 20% = 200
    assertEquals(0, new BigDecimal("200.00").compareTo(result.value()));
}

Best Practices

  1. Use BigDecimal for precise monetary calculations
  2. Always specify rounding mode for division operations
  3. Validate units when adding/subtracting quantities
  4. Use factory methods (pln(), eur()) for readability
  5. Avoid floating-point arithmetic for money

Common Patterns

Money as Value Object

Money is immutable - operations return new instances

Type Safety

Quantities enforce unit compatibility at runtime

Precision

Uses BigDecimal to avoid floating-point errors

Multi-Currency

Full support for different currencies via JavaMoney
  • Used extensively in Pricing for calculations
  • Accounting uses Money for all financial transactions
  • Ordering uses Quantity for order line quantities
  • Inventory uses Quantity for stock levels

Build docs developers (and LLMs) love