Skip to content

Implement IDR rate aggregator#207

Open
nflhm wants to merge 1 commit into
allobankdev:mainfrom
nflhm:feat/idr-rate-aggregator
Open

Implement IDR rate aggregator#207
nflhm wants to merge 1 commit into
allobankdev:mainfrom
nflhm:feat/idr-rate-aggregator

Conversation

@nflhm
Copy link
Copy Markdown

@nflhm nflhm commented Apr 2, 2026

Summary

This PR implements a Spring Boot REST API that aggregates data from Frankfurter Exchange Rate API. The application exposes a single polymorphic endpoint: {GET /api/finance/data/{resourceType}}. where {resourceType} can be one of:

  • latest_idr_rates – latest exchange rates with IDR as base, plus a custom USD_BuySpread_IDR field calculated using the GitHub username.
  • historical_idr_usd – IDR → USD rates for the fixed date range.
  • supported_currencies – list of all currency symbols supported by the Frankfurter API.

Architectural Rationale

  1. Polymorphism Justification – Strategy Pattern
    The application supports three distinct data sources (latest_idr_rates, historical_idr_usd, supported_currencies), each requiring different logic for fetching and transforming data. Using the Strategy Pattern encapsulates each variant in its own class, implementing a common interface (IDRDataFetcher). The controller selects the appropriate strategy via a map lookup (injected by Spring).

    Benefits:

    • Extensibility: Adding a new resource type only requires creating a new strategy class; no existing code needs modification.
    • Maintainability: Each strategy is isolated, making it easier to test, debug, and modify individual behaviors.
    • Avoids Conditional Logic: Eliminates the need for if-else or switch statements in the controller or service layer, keeping the code clean and reducing complexity.
  2. Client Factory – Why FactoryBean?
    A custom FactoryBean is used to create the WebClient instance. This factory centralizes configuration (base URL, timeouts, headers) and can be reused across all strategies.

    Advantages over a standard @bean method:

    • Encapsulation of Complex Setup: The FactoryBean can perform additional logic (e.g., validation, conditional configuration) that would otherwise clutter a simple @bean definition.
    • Separation of Concerns: The factory bean is responsible only for client creation, keeping the configuration class clean.
    • Testability: The factory can be easily mocked or replaced with a test double, allowing unit tests to inject a custom WebClient.
  3. Startup Runner – ApplicationRunner over @PostConstruct
    The initial data load is performed in an ApplicationRunner component.

    Why not @PostConstruct?
    @PostConstruct runs before the application context is fully initialized; if any dependencies (e,g., WebClient) are not yet ready, the operation may fail.
    ApplicationRunner guarantees that all beans are fully instantiated and the context is ready before execution.

    Benefits:

    • Reliability: The data load happens after the entire application is ready, reducing the risk of missing dependencies.
    • Error Handling: Failures during data loading can be managed to prevent the application from starting with incomplete data (or to allow startup with graceful fallback).

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