Thank you for applying to our team! This take-home test is designed to evaluate your practical skills in building production-ready Spring Boot applications within a finance domain, focusing on architectural patterns and complex data handling.
Your task is to create a single Spring Boot REST API endpoint capable of aggregating data from multiple, distinct resources provided by the public, keyless Frankfurter Exchange Rate API. The primary focus is on handling Indonesian Rupiah (IDR) data.
The focus of this test is not just functional correctness, but demonstrating clean code, advanced Spring concepts, thread-safe design, and architectural clarity.
-
Base URL (Public):
https://api.frankfurter.app/. -
You must integrate with three distinct data resources to enforce the architectural pattern:
-
/latest?base=IDR(The latest rates relative to IDR) -
Historical Data: Query a specific, small time series (e.g.,
/2024-01-01..2024-01-05?from=IDR&to=USD). Note: Use the date range provided in this example unless a different range is communicated separately. -
/currencies(The list of all supported currency symbols)
-
You must expose one single endpoint in your application: GET /api/finance/data/{resourceType}
Where {resourceType} can be one of the three strings: latest_idr_rates, historical_idr_usd, or supported_currencies.
-
Resource Handling: Your service must correctly map the three incoming
resourceTypevalues to the correct data fetching strategies. -
Data Load: All three resources should be fetched from the external API.
-
Data Transformation (Latest IDR Rates only) - Unique Calculation: For the
latest_idr_ratesresource, you must calculate and include a new field,"USD_BuySpread_IDR". This is the Rupiah selling rate to USD after applying a banking spread/margin.The Spread Factor Must Be Unique :
- Input: Your GitHub username (e.g.,
johndoe47). - Calculation: Calculate the sum of the Unicode (ASCII) values of all characters in your lowercase GitHub username string.
- Spread Factor Derivation:
Spread Factor = (Sum of Unicode Values % 1000) / 100000.0(This will yield a unique factor between 0.00000 and 0.00999, ensuring a personalized result.)
Final Formula:
USD_BuySpread_IDR = (1 / Rate_USD) * (1 + Spread Factor)(whereRate_USDis the value from the API whenbase=IDR). - Input: Your GitHub username (e.g.,
-
Other Resources: The
historical_idr_usdandsupported_currenciesresources can return their data with minimal transformation, but the final output must be a unified JSON array of results.
Meeting the core task is only one part of the solution. The following constraints must be strictly adhered to and will be heavily weighted during evaluation:
The logic for handling the three different resources (latest_idr_rates, historical_idr_usd, supported_currencies) must be implemented using the Strategy Design Pattern.
-
Define a clear Strategy Interface (e.g.,
IDRDataFetcher). -
Implement three concrete strategy classes (one for each resource).
-
The main
Controllershould dynamically select the correct strategy implementation using a map-based lookup injected by Spring, avoiding any manualif/elseorswitchlogic in the controller layer.
The instance of your chosen external API client (WebClient or RestTemplate) must be defined and created within a custom implementation of Spring's FactoryBean<T> interface.
-
This
FactoryBeanshould be responsible for externalizing the API Base URL via@Valueor@ConfigurationPropertiesand applying any initial configuration (e.g., timeouts, shared headers). -
You may not define the client as a simple
@Beanin a@Configurationclass.
The aggregated data for ALL three resources must be fetched exactly once on application startup and loaded into an in-memory store.
-
Use a Spring Boot
ApplicationRunnerorCommandLineRunnercomponent to initiate the data fetching process. -
The API endpoint (
GET /api/finance/data/{resourceType}) must serve the data from this in-memory store, not by making a new call to the external API on every request. -
The in-memory storage mechanism (e.g., a service holding the data) must be designed to be thread-safe and ensure the data is immutable once the
ApplicationRunnerhas finished loading it.
Your final solution must demonstrate production quality through code, testing, and communication.
-
Graceful Error Handling for network failures or 4xx/5xx responses from the external API.
-
Proper use of Configuration Properties (e.g.,
application.yml) for external service URLs. -
Clear separation of concerns (Controller, Service, Model/DTO, etc.).
-
Unit Tests for all three
IDRDataFetcherstrategy implementations, ensuring data calculation and transformation logic is covered (using mock clients for external calls). -
Integration Tests to verify the
ApplicationRunnersuccessfully initializes and loads the data into the in-memory store before the application context is ready.
A clear README.md is mandatory. It must include:
-
Setup/Run Instructions: Clear steps to clone, build, and run the application and tests.
-
Endpoint Usage: Example cURL commands to test the three different resource types.
-
Personalization Note: Clearly state your GitHub username and show the exact Spread Factor (e.g.,
0.00765) calculated by your function. -
-
This section should contain a brief, but detailed, explanation answering the following questions:
-
Polymorphism Justification: Explain why the Strategy Pattern was used over a simpler conditional block in the service layer for handling the multi-resource endpoint. Discuss the benefits in terms of extensibility and maintainability.
-
Client Factory: Explain the specific role and benefit of using a
FactoryBeanto construct the external API client. Why is this preferable to defining the client using a standard@Beanmethod in this scenario? -
Startup Runner Choice: Justify the choice of using an
ApplicationRunner(orCommandLineRunner) for the initial data ingestion over a simpler@PostConstructmethod.
-
-
Fork this repository.
-
Implement your solution on a dedicated feature branch (e.g.,
feat/idr-rate-aggregator). -
When complete, submit your solution via a Pull Request (PR) back to the main repository.
-
Please complete the form to submit your technical test: Click Here
Your PR will be evaluated on the following:
-
Commit History: Clean, atomic, and descriptive commit messages (e.g., "feat: Implement IDR latest rates strategy," "fix: Correctly calculate IDR spread in tests").
-
PR Description: The description must clearly summarize the solution and must contain the full answers to the three "Architectural Rationale" questions from Section III.
-
Code Review Readiness: The code should be well-structured and ready for immediate review.
Good luck!