Money
Namespace: RA.Utilities.Core.ValueObjects
The Money value object pairs a monetary Amount (decimal) with a Currency. It guards arithmetic so amounts in different currencies can never be combined accidentally.
🎯 Purpose
A bare decimal cannot express what it represents: 100 of what? Combining amounts across currencies silently is one of the most expensive bugs in financial software. Money makes the currency part of the type and enforces the "same currency" rule at runtime, while decimal preserves the precision that double and float cannot.
Properties
| Property | Type | Description |
|---|---|---|
| Amount | decimal | The numeric monetary amount. |
| Currency | Currency | The currency of the amount. |
Members
| Member | Description |
|---|---|
Money(decimal amount, Currency currency) | Creates a Money instance. Validation-free; see PositiveMoney. |
PositiveMoney(decimal, Currency) | Factory that throws BadRequestException when the amount is negative. |
Add(Money other) | Returns a new Money with the sum; throws BadRequestException when currencies differ. |
Subtract(Money other) | Returns a new Money with the difference; throws BadRequestException when currencies differ. |
ToString() | Returns "<Amount> <Currency>", e.g. 24.98 EUR. |
Validation rules
| Rule | ErrorCode (from BaseErrorCode) | Notes |
|---|---|---|
Negative amount in PositiveMoney | PositiveMoney (POSITIVE_MONEY) | PropertyName is Amount, AttemptedValue is the amount |
Different currencies in Add / Subtract | CurrencyMismatch (CURRENCY_MISMATCH) | AttemptedValue is the other currency, ExpectedValue is the current one |
All failures are reported as a BadRequestException whose Errors collection contains a single ValidationError.
🚀 How to Use
Create positive amounts
using RA.Utilities.Core.ValueObjects;
var price = Money.PositiveMoney(19.99m, new Currency("EUR"));
var shipping = Money.PositiveMoney(4.99m, new Currency("EUR"));
Money.PositiveMoney(-1m, new Currency("EUR")); // throws BadRequestException
Add amounts safely
var total = price.Add(shipping); // Money { Amount = 24.98, Currency = EUR }
total.ToString(); // "24.98 EUR"
total.Add(new Money(5m, new Currency("USD"))); // throws BadRequestException (CurrencyMismatch)
Add returns a new instance; Money is immutable, like all value objects in this package.
Subtract amounts safely
var price = Money.PositiveMoney(19.99m, new Currency("EUR"));
var discount = Money.PositiveMoney(4.00m, new Currency("EUR"));
var net = price.Subtract(discount); // Money { Amount = 15.99, Currency = EUR }
net.ToString(); // "15.99 EUR"
price.Subtract(new Money(1m, new Currency("USD"))); // throws BadRequestException (CurrencyMismatch)
Subtract mirrors Add: it returns a new instance and throws the same CurrencyMismatch error when the currencies differ.
Equality
new Money(10m, new Currency("EUR")) == new Money(10m, new Currency("EUR")); // true
new Money(10m, new Currency("EUR")) == new Money(10m, new Currency("USD")); // false
Unlike the string-based value objects, Money has no built-in JSON converter and serializes as a regular object with Amount and Currency properties. If you need a custom representation (e.g. "24.98 EUR" or a { amount, currency } contract), write a dedicated JsonConverter<Money> in your application.