Skip to content

Feat/idr rate aggregator ians#161

Open
herdiansyah5197 wants to merge 9 commits intoallobankdev:mainfrom
herdiansyah5197:feat/idr-rate-aggregator-ians
Open

Feat/idr rate aggregator ians#161
herdiansyah5197 wants to merge 9 commits intoallobankdev:mainfrom
herdiansyah5197:feat/idr-rate-aggregator-ians

Conversation

@herdiansyah5197
Copy link

This PR implements the Allo Bank Backend Developer Take-Home Test requirements.
The application integrates with the public Frankfurter API and exposes a single polymorphic endpoint:

GET /api/finance/data/{resourceType}

Supported resource types:

  • latest_idr_rates
  • historical_idr_usd
  • supported_currencies

What Was Implemented

  1. Strategy Pattern (Constraint A)

Created IDRDataFetcher interface.
Implemented three concrete strategies:

  • Latest IDR Rates
  • Historical IDR → USD
  • Supported Currencies

Controller selects strategy dynamically using Spring-injected Map<String, IDRDataFetcher>.
No if/else or switch logic in controller.
This makes the solution extensible and clean.

  1. Custom FactoryBean (Constraint B)

Implemented a custom FactoryBean for the external API client.
Base URL and timeout are externalized using @ConfigurationProperties.
Centralized client configuration and error handling.
Client is not defined as a simple @bean.

  1. Startup Data Loading & Immutability (Constraint C)

Used ApplicationRunner to fetch all three resources once at application startup.
Data is stored in an in-memory storage component.
Storage is thread-safe and immutable after initialization.
API endpoint serves data from memory (no external calls per request).

  1. Business Logic

For latest_idr_rates, implemented unique spread calculation based on GitHub username:
Spread Factor = (Sum of ASCII values % 1000) / 100000.0
USD_BuySpread_IDR = (1 / Rate_USD) * (1 + Spread Factor)

The calculated value is included in the response.

Testing
Unit tests for all strategy implementations.
Mocked external API calls.
Verified spread calculation logic.
Verified startup data loading.

Architectural Rationale
Why Strategy Pattern?

It keeps the controller clean and follows the Open/Closed Principle.
New resource types can be added without modifying existing logic.

Why FactoryBean?

It encapsulates client creation logic and configuration in one place, providing better lifecycle control than a simple @bean.

Why ApplicationRunner?

It runs after the Spring context is fully initialized and is more appropriate for startup workflows compared to @PostConstruct.

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