Skip to content

feat: implement IDR Rate Aggregator service using Frankfurter API#176

Open
hosea-adrianus wants to merge 6 commits intoallobankdev:mainfrom
hosea-adrianus:feat/idr-rate-aggregator
Open

feat: implement IDR Rate Aggregator service using Frankfurter API#176
hosea-adrianus wants to merge 6 commits intoallobankdev:mainfrom
hosea-adrianus:feat/idr-rate-aggregator

Conversation

@hosea-adrianus
Copy link
Copy Markdown

@hosea-adrianus hosea-adrianus commented Feb 27, 2026

Description

This PR introduces the IDR Rate Aggregator Service, responsible for fetching and preloading exchange rate data from Frankfurter API which consist of 3 API Endpoint:

  1. Latest IDR Rates - GET /api/finance/data/latest_idr_rates
{
  "amount": 1,
  "base": "IDR",
  "date": "2026-02-26",
  "rates": {
    "AUD": 0.000084,
    "BRL": 0.00031,
    "CAD": 0.000082,
    "CHF": 0.000046,
    "CNY": 0.00041,
    "CZK": 0.00122,
    "DKK": 0.00038,
    "EUR": 0.00005,
    "GBP": 0.000044,
    "HKD": 0.00047,
    "HUF": 0.01894,
    "ILS": 0.00019,
    "INR": 0.00542,
    "ISK": 0.00723,
    "JPY": 0.00931,
    "KRW": 0.08502,
    "MXN": 0.00102,
    "MYR": 0.00023,
    "NOK": 0.00057,
    "NZD": 0.0001,
    "PHP": 0.00344,
    "PLN": 0.00021,
    "RON": 0.00026,
    "SEK": 0.00054,
    "SGD": 0.000075,
    "THB": 0.00185,
    "TRY": 0.00262,
    "USD": 0.00006,
    "ZAR": 0.00095
  },
  "USD_BuySpread_IDR": 16738
}
  1. Historical IDR-USD Rates – GET /api/finance/data/historical_idr_usd
{
  "amount": 1,
  "base": "IDR",
  "rates": {
    "2023-12-29": {
      "USD": 0.000065
    },
    "2024-01-02": {
      "USD": 0.000064
    },
    "2024-01-03": {
      "USD": 0.000064
    },
    "2024-01-04": {
      "USD": 0.000064
    },
    "2024-01-05": {
      "USD": 0.000064
    }
  },
  "startDate": "2023-12-29",
  "endDate": "2024-01-05"
}
  1. Supported Currencies - GET /api/finance/data/supported_currencies
{
  "AUD": "Australian Dollar",
  "BRL": "Brazilian Real",
  "CAD": "Canadian Dollar",
  "CHF": "Swiss Franc",
  "CNY": "Chinese Renminbi Yuan",
  "CZK": "Czech Koruna",
  "DKK": "Danish Krone",
  "EUR": "Euro",
  "GBP": "British Pound",
  "HKD": "Hong Kong Dollar",
  "HUF": "Hungarian Forint",
  "IDR": "Indonesian Rupiah",
  "ILS": "Israeli New Sheqel",
  "INR": "Indian Rupee",
  "ISK": "Icelandic Króna",
  "JPY": "Japanese Yen",
  "KRW": "South Korean Won",
  "MXN": "Mexican Peso",
  "MYR": "Malaysian Ringgit",
  "NOK": "Norwegian Krone",
  "NZD": "New Zealand Dollar",
  "PHP": "Philippine Peso",
  "PLN": "Polish Złoty",
  "RON": "Romanian Leu",
  "SEK": "Swedish Krona",
  "SGD": "Singapore Dollar",
  "THB": "Thai Baht",
  "TRY": "Turkish Lira",
  "USD": "United States Dollar",
  "ZAR": "South African Rand"
}

Key features:

  • Strategy Pattern is used to handle each resource type independently, improving extensibility and maintainability.
  • ApplicationRunner ensures preloading of all data into FinanceDataStore at application startup.
  • All fetched data is stored as immutable.

Architectural Rationale

i. Polymorphism Justification

The Strategy Pattern is used to handle the multi-resource endpoint so that each resource type (historical, latest, currencies) has its own implementation, avoiding bulky if-else or switch statements in the service layer.
Benefits:

  • Extensibility: Adding a new resource only requires creating a new class implementing IDRDataFetcher, without changing the service.
  • Maintainability: Each resource’s logic is isolated, making it easier to test and maintain.

ii. Client Factory

A FactoryBean is used to construct the FrankfurterWebClient with dynamic configurations like baseUrl and ObjectMapper strategy.
Advantages over a simple @bean:

  • Encapsulates client creation logic, enabling shared configuration (e.g., interceptors, error handling) without redundant setup for each client instance.

iii. Startup Runner Choice

ApplicationRunner is chosen to preload data at application startup because:

  • It runs after the full Spring context is initialized (all dependencies injected).
  • It is safer than @PostConstruct, which executes immediately after bean creation, before the full context is ready, ensuring external clients and services are available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant