converter/domain/entities.go

132 lines
3 KiB
Go
Raw Permalink Normal View History

2025-09-06 08:20:05 +03:00
package domain
import (
"errors"
"fmt"
"strings"
"time"
"github.com/shopspring/decimal"
)
// Domain-specific errors
var (
ErrInvalidCurrency = errors.New("invalid currency")
ErrInvalidAmount = errors.New("invalid amount")
ErrNegativeAmount = errors.New("amount cannot be negative")
ErrInvalidRate = errors.New("invalid exchange rate")
ErrCurrencyMismatch = errors.New("currency mismatch")
ErrRateNotFound = errors.New("exchange rate not found")
ErrUnsupportedCurrency = errors.New("unsupported currency")
)
// Currency represents a currency with its metadata
type Currency struct {
Code string
Name string
Precision int
}
// NewCurrency creates a new Currency with validation
func NewCurrency(code, name string, precision int) (Currency, error) {
if strings.TrimSpace(code) == "" {
return Currency{}, ErrInvalidCurrency
}
if precision < 0 {
return Currency{}, ErrInvalidCurrency
}
return Currency{
Code: strings.ToUpper(strings.TrimSpace(code)),
Name: strings.TrimSpace(name),
Precision: precision,
}, nil
}
// String returns the currency code
func (c Currency) String() string {
return c.Code
}
// Money represents an amount in a specific currency
type Money struct {
Amount decimal.Decimal
Currency Currency
}
// NewMoney creates a new Money instance from string amount
func NewMoney(amount string, currency Currency) (Money, error) {
if strings.TrimSpace(amount) == "" {
return Money{}, ErrInvalidAmount
}
decimalAmount, err := decimal.NewFromString(strings.TrimSpace(amount))
if err != nil {
return Money{}, ErrInvalidAmount
}
if decimalAmount.IsNegative() {
return Money{}, ErrNegativeAmount
}
return Money{
Amount: decimalAmount,
Currency: currency,
}, nil
}
// NewMoneyFromDecimal creates a new Money instance from decimal.Decimal
func NewMoneyFromDecimal(amount decimal.Decimal, currency Currency) (Money, error) {
if amount.IsNegative() {
return Money{}, ErrNegativeAmount
}
return Money{
Amount: amount,
Currency: currency,
}, nil
}
// String returns formatted money representation
func (m Money) String() string {
return fmt.Sprintf("%s %s", m.Amount.StringFixed(int32(m.Currency.Precision)), m.Currency.Code)
}
// Rate represents an exchange rate between two currencies
type Rate struct {
From Currency
To Currency
Value decimal.Decimal
Timestamp time.Time
Source string
}
// NewRate creates a new Rate with validation
func NewRate(from, to Currency, value decimal.Decimal, source string) (Rate, error) {
if from.Code == to.Code {
return Rate{}, ErrInvalidRate
}
if value.IsNegative() || value.IsZero() {
return Rate{}, ErrInvalidRate
}
return Rate{
From: from,
To: to,
Value: value,
Timestamp: time.Now(),
Source: strings.TrimSpace(source),
}, nil
}
// String returns formatted rate representation
func (r Rate) String() string {
return fmt.Sprintf("1 %s = %s %s",
r.From.Code,
r.Value.StringFixed(int32(r.To.Precision)),
r.To.Code)
}