Skip to content

Currency System

โš™๏ธ Overview

Dynamic Currency Management

The fintech application supports dynamic currency management, allowing currencies to be added, updated, and configured at runtime without requiring code changes or system restarts.

๐Ÿ“ฆ Architecture

๐Ÿ—๏ธ Currency Registry (pkg/currency/)

The currency registry is the core component that manages currency metadata:

type Meta struct {
    Decimals int    // Number of decimal places (e.g., 2 for USD, 8 for BTC)
    Symbol   string // Currency symbol (e.g., "$", "โ‚ฟ", "โ‚ฌ")
}

type Registry struct {
    currencies map[string]Meta
    mu         sync.RWMutex  // Thread-safe access
}

๐Ÿ—ƒ๏ธ Global Registry

A global registry instance provides convenience functions:

// Global convenience functions
currency.Get("USD")           // Get currency metadata
currency.Register("BTC", meta) // Add/update currency
currency.IsSupported("EUR")   // Check if currency is supported
currency.ListSupported()      // Get all supported currencies

๐Ÿš€ Dynamic Currency Feature

Register and Update Currencies at Runtime

You can register new currencies or update existing ones on the fly, supporting both fiat and crypto use cases.

๐Ÿ Runtime Currency Registration

Add new currencies without restarting the application:

// Register cryptocurrencies
currency.Register("BTC", currency.Meta{Decimals: 8, Symbol: "โ‚ฟ"})
currency.Register("ETH", currency.Meta{Decimals: 18, Symbol: "ฮž"})

// Register new fiat currencies
currency.Register("BRL", currency.Meta{Decimals: 2, Symbol: "R$"})

๐Ÿ› ๏ธ Currency Updates

Update existing currency configurations:

// Update USD to support 3 decimal places for micro-transactions
currency.Register("USD", currency.Meta{Decimals: 3, Symbol: "$"})

// Update JPY to support decimal places
currency.Register("JPY", currency.Meta{Decimals: 2, Symbol: "ยฅ"})

๐Ÿง‘โ€๐Ÿ’ผ Multi-Tenant Support

Different tenants can have different currency configurations:

// Tenant A: Traditional banking
tenantARegistry := currency.NewRegistry()
tenantARegistry.Register("USD", currency.Meta{Decimals: 2, Symbol: "$"})
tenantARegistry.Register("EUR", currency.Meta{Decimals: 2, Symbol: "โ‚ฌ"})

// Tenant B: Cryptocurrency exchange
tenantBRegistry := currency.NewRegistry()
tenantBRegistry.Register("BTC", currency.Meta{Decimals: 8, Symbol: "โ‚ฟ"})
tenantBRegistry.Register("ETH", currency.Meta{Decimals: 18, Symbol: "ฮž"})

๐Ÿ›ก๏ธ Graceful Fallback

Unknown Currencies

If you request metadata for an unknown currency, the system returns a default configuration. Always validate currency codes to avoid surprises.

// Unknown currency returns default configuration
unknownInfo := currency.Get("UNKNOWN_CURRENCY")
// Returns: Meta{Decimals: 2, Symbol: "UNKNOWN_CURRENCY"}

๐Ÿฆ Domain Integration

๐Ÿฆ Account Creation with Dynamic Currencies

// Create accounts with any registered currency
btcAccount, err := domain.NewAccountWithCurrency(userID, "BTC")
ethAccount, err := domain.NewAccountWithCurrency(userID, "ETH")

๐Ÿ’ฐ Money Operations with Dynamic Currencies

Precision Enforcement

Each currency enforces its own decimal precision. Attempting to use more decimals than allowed will result in an error.

// Create money objects with dynamic currencies
btcMoney, err := money.New(0.001, "BTC")  // 0.001 BTC
ethMoney, err := money.New(0.5, "ETH")    // 0.5 ETH

// Perform operations
_, err = btcAccount.Deposit(userID, btcMoney)
balance, err := btcAccount.GetBalance(userID)

๐Ÿงฎ Precision Handling

Each currency maintains its own precision rules:

// USD: 2 decimal places
usdMoney, _ := domain.NewMoney(100.99, "USD")     // Valid
usdMoney, _ := domain.NewMoney(100.999, "USD")    // Error: too many decimals

// JPY: 0 decimal places
jpyMoney, _ := domain.NewMoney(1000, "JPY")       // Valid
jpyMoney, _ := domain.NewMoney(1000.5, "JPY")     // Error: decimals not allowed

// BTC: 8 decimal places
btcMoney, _ := domain.NewMoney(0.00000001, "BTC") // Valid: 1 satoshi

๐ŸŒ Real-World Use Cases

๐ŸŒ Cryptocurrency Exchange

// Register cryptocurrencies as they become available
currency.Register("BTC", currency.Meta{Decimals: 8, Symbol: "โ‚ฟ"})
currency.Register("ETH", currency.Meta{Decimals: 18, Symbol: "ฮž"})
currency.Register("USDT", currency.Meta{Decimals: 6, Symbol: "โ‚ฎ"})
currency.Register("ADA", currency.Meta{Decimals: 6, Symbol: "โ‚ณ"})

// Create accounts for each cryptocurrency
btcAccount := domain.NewAccountWithCurrency(userID, "BTC")
ethAccount := domain.NewAccountWithCurrency(userID, "ETH")

๐ŸŒ International Banking

// Support new national currencies
currency.Register("TRY", currency.Meta{Decimals: 2, Symbol: "โ‚บ"}) // Turkish Lira
currency.Register("INR", currency.Meta{Decimals: 2, Symbol: "โ‚น"}) // Indian Rupee
currency.Register("BRL", currency.Meta{Decimals: 2, Symbol: "R$"}) // Brazilian Real

Micro-Transaction Support

Update USD to support 3 decimal places for micro-transactions:

currency.Register("USD", currency.Meta{Decimals: 3, Symbol: "$"})
microMoney, _ := domain.NewMoney(0.001, "USD")

๐Ÿ”„ Currency Migration

// Scenario: Migrating from 2 to 3 decimal places for USD
// Step 1: Update the currency configuration
currency.Register("USD", currency.Meta{Decimals: 3, Symbol: "$"})

// Step 2: Existing accounts continue to work
// Step 3: New operations use the updated configuration
newMoney, _ := domain.NewMoney(100.999, "USD") // Now valid

๐Ÿ› ๏ธ Configuration Management

2. Precision Validation

// Too many decimal places
money, err := domain.NewMoney(100.999, "USD")
if err != nil {
    // err contains precision error message
}

3. Currency Mismatch

// Cannot add different currencies
usdMoney, _ := money.New(100, "USD")
eurMoney, _ := money.New(100, "EUR")
sum, err := usdMoney.Add(eurMoney)
if err != nil {
    // err == domain.ErrInvalidCurrencyCode
}

โšก Performance Considerations

1. Registry Lookups

  • Currency lookups are O(1) hash map operations
  • Thread-safe with minimal lock contention
  • Global registry uses read-write mutex for optimal performance

2. Memory Usage

  • Each currency metadata is small (~24 bytes)
  • Registry grows linearly with number of currencies
  • Default currencies are pre-loaded for fast access

3. Concurrency

  • Read operations use read locks (shared access)
  • Write operations use write locks (exclusive access)
  • Designed for high read-to-write ratios

๐Ÿ… Best Practices

1. Currency Registration

// Register currencies early in application startup
func init() {
    // Register default currencies
    currency.Register("USD", currency.Meta{Decimals: 2, Symbol: "$"})
    currency.Register("EUR", currency.Meta{Decimals: 2, Symbol: "โ‚ฌ"})

    // Load additional currencies from configuration
    LoadCurrenciesFromConfig("currencies.json")
}

2. Validation

// Always validate currency codes before use
if !currency.IsSupported(currencyCode) {
    return fmt.Errorf("unsupported currency: %s", currencyCode)
}

3. Error Handling

// Handle currency-related errors gracefully
money, err := domain.NewMoney(amount, currencyCode)
if err != nil {
    // Log error and return appropriate response
    return fmt.Errorf("invalid money amount: %w", err)
}

๐Ÿงช Testing

1. Unit Tests

func TestDynamicCurrency(t *testing.T) {
    // Register test currency
    currency.Register("TEST", currency.Meta{Decimals: 2, Symbol: "T"})

    // Test account creation
    account, err := domain.NewAccountWithCurrency(userID, "TEST")
    assert.NoError(t, err)

    // Test money operations
    money, err := domain.NewMoney(100.50, "TEST")
    assert.NoError(t, err)

    // Test account operations
    _, err = account.Deposit(userID, money)
    assert.NoError(t, err)
}

2. Integration Tests

func TestCurrencyHotReload(t *testing.T) {
    // Test initial state
    money1, _ := domain.NewMoney(100.99, "USD")

    // Update currency configuration
    currency.Register("USD", currency.Meta{Decimals: 3, Symbol: "$"})

    // Test new configuration
    money2, _ := domain.NewMoney(100.999, "USD")
    assert.NotEqual(t, money1.String(), money2.String())
}

๐ŸŽฏ Conclusion

The dynamic currency system provides:

  1. Flexibility: Add currencies without code changes
  2. Scalability: Support for unlimited currencies
  3. Thread Safety: Safe concurrent access
  4. Performance: Fast lookups and minimal overhead
  5. Maintainability: Clean separation of concerns
  6. Extensibility: Easy to add new features

This system enables the fintech application to adapt to changing business requirements and support new currencies as they emerge in the market.