4 min read

Fowler's Money Pattern: how to make the world go round

Discover Fowler's Money Pattern, the solution to floating-point arithmetic woes in currency calculations. Learn how to implement it in TypeScript and compare approaches with popular libraries. Say goodbye to rounding errors and currency mix-ups, and hello to precise, type-safe money handling.
Fowler's Money Pattern: how to make the world go round
Photo by Kenny Eliason / Unsplash

Back in university, I worked on an accounting system for my final year project. Little did I know that implementing money would change my brain chemistry forever. You see, I had that moment—the one where your code decides 0.1 + 0.2 ≠ 0.3, and you start questioning your life choices.

Friends, I welcome you to the wonderful world of floating-point arithmetic. If you're dealing with money in your code, here's yet another pattern to consider: Fowler's Money Pattern.

What's the Big Deal?

First off, if you're using floats or doubles for currency, stop for a moment and reconsider.

// Don't do this. I'm serious.
const price: number = 19.99

Why? Floating-point arithmetic is like that friend who's "about 5 minutes away" - it's an approximation, and it'll bite you when you least expect it.

The Floating-Point Guide - What Every Programmer Should Know About Floating-Point Arithmetic

Fowler's Money Pattern

Martin Fowler, in his book "Patterns of Enterprise Application Architecture", introduced a pattern that's been saving developers' weekends ever since. Here's the gist:

  1. Use integers to represent the smallest currency unit (e.g., cents).
  2. Create a Money class that encapsulates the amount and currency.
  3. Keep currency operations separate.

Let's break it down.

Use Integers

Instead of dealing with dollars and cents separately, just use cents. It's like counting sheep - you don't count 2.5 sheep, do you?

// Instead of this:
const price: number = 19.99

// Do this:
const priceInCents: number = 1999

The Money Class

Create a Money class that holds the amount and currency. Make it immutable - because who needs the headache of mutable state?

class Money {
    private readonly amountInCents: number
    private readonly currency: string

    constructor(amountInCents: number, currency: string) {
        this.amountInCents = amountInCents
        this.currency = currency
    }

    add(other: Money): Money {
        if (this.currency !== other.currency) {
            throw new Error("Cannot add different currencies")
        }
        return new Money(this.amountInCents + other.amountInCents, this.currency)
    }

    getAmountInCents(): number {
        return this.amountInCents
    }

    getCurrency(): string {
        return this.currency
    }

    toString(): string {
        return `${(this.amountInCents / 100).toFixed(2)} ${this.currency}`
    }
}

Now you can do things like:

const price = new Money(1999, "MYR")
const discount = new Money(500, "MYR")
const finalPrice = price.add(discount)
console.log(finalPrice.toString()) // "24.99 MYR"

And when you try to sum two different currency type:

const price = new Money(1999, "MYR")
const otherCurrency = new Money(550, "USD")

finalPrice = finalPrice.add(otherCurrency) // Error: "Cannot add different currencies"

With type checking baked in, there'd be no more accidental Ringgit + Dollar additions.

Why Though?

  1. Precision: No more rounding errors. 19.99 + 0.01 will always be 20.00.
  2. Type Safety: Try adding USD to EUR (Spoiler: It'll fail, as it should)
  3. Clarity: Your code intentionally describes that "This is actually money, the moniest money" instead of "This might be money, or it might be the weight I gained last Raya"

Currency Conversion

This is an entire scope not included in Fowler's Money Pattern. Keep currency conversion separate. Your Money class shouldn't be concerned with exchange rates. Here's how you can separate it:

interface ExchangeRates {
    [key: string]: number
}

class CurrencyConverter {
    private rates: ExchangeRates

    constructor(rates: ExchangeRates) {
        this.rates = rates
    }

    convert(money: Money, targetCurrency: string): Money {
        const rate = this.rates[`${money.getCurrency()}_${targetCurrency}`]
        if (!rate) {
            throw new Error(`Exchange rate not found for ${money.getCurrency()} to ${targetCurrency}`)
        }
        const newAmount = Math.round(money.getAmountInCents() * rate)
        return new Money(newAmount, targetCurrency)
    }
}

// Usage
const rates = { "MYR_USD": 0.22 }
const converter = new CurrencyConverter(rates)
const myrAmount = new Money(10000, "MYR")
const usdAmount = converter.convert(myrAmount, "USD")
console.log(usdAmount.toString()) // "22.00 USD"

You can even extend this to include when the exchange rate was recorded. This would make it easier to track. For example, reflecting the exchange rate on the invoice or past purchases would clarify to the customer how the final amount is calculated.

How XYZ Do It

Let's examine how some popular libraries implement Fowler's Money Pattern. This can provide insight into different approaches to solving the same problem.

currency.js

currency.js implements the Money Pattern with some unique characteristics:

function currency(value, opts) {
  var that = this
  if(!(that instanceof currency)) {
    return new currency(value, opts)
  }
  var settings = Object.assign({}, defaults, opts)
    , precision = pow(10, settings.precision)
  that.intValue = rounds(value * precision)
  that.value = that.intValue / precision
  that._s = settings
}

Key points:

  1. It uses an intValue for internal representation, similar to the cents approach.
  2. Operations return new objects, maintaining immutability.
  3. It allows for custom precision settings.

currency.js focuses on calculation precision and formatting. It provides flexibility in handling different currencies and formatting options.

currency.prototype = {
  add(number) {
      let { intValue, _settings, _precision } = this;
      return currency((intValue += parse(number, _settings)) / (_settings.fromCents ? 1 : _precision), _settings);
    },  
}

go-money

go-money, a Go library, implements the Money Pattern as follows:

// Money represents monetary value information, stores
// currency and amount value.
type Money struct {
	amount   Amount    `db:"amount"`
	currency *Currency `db:"currency"`
}

// New creates and returns new instance of Money.
func New(amount int64, code string) *Money {
	return &Money{
		amount:   amount,
		currency: newCurrency(code).get(),
	}
}

Go-money's implementation features:

  1. Use of int64 for the amount, representing the smallest currency unit.
  2. Coupling of currency with the amount in the Money struct.
  3. Explicit handling of operations between different currencies.
// Add returns new Money struct with value representing sum of Self and Other Money.
func (m *Money) Add(ms ...*Money) (*Money, error) {
	if len(ms) == 0 {
		return m, nil
	}

	k := New(0, m.currency.Code)

	for _, m2 := range ms {
		if err := m.assertSameCurrency(m2); err != nil {
			return nil, err
		}

		k.amount = mutate.calc.add(k.amount, m2.amount)
	}

	return &Money{amount: mutate.calc.add(m.amount, k.amount), currency: m.currency}, nil
}

go-money enforces currency type safety by checking currency codes before performing operations.

Conclusion

Implementing Fowler's Money Pattern might seem like overkill for small projects. But trust me, future you will be grateful when you're not debugging financial discrepancies at 2 AM on a Sunday. Like I was, when trying to hit the FYP deadline, sigh. Imagine rewriting money handling after being done with invoicing, journal and ledger, double sigh. Got an A though.

Remember: In the world of programming, money is serious business.