Feature/idr rate aggregator test#214
Open
furqonfajri wants to merge 10 commits into
Open
Conversation
…ree concrete strategies
…onRunner for startup data loading
…or ApplicationRunner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ringkasan Solusi
REST API Spring Boot untuk mengagregasi data exchange rate IDR dari Frankfurter API.
Satu endpoint
GET /api/finance/data/{resourceType}melayani 3 resource type:latest_idr_rates→ data rates IDR terbaru + kalkulasi USD_BuySpread_IDRhistorical_idr_usd→ data historis IDR/USD periode 2024-01-01 s/d 2024-01-05supported_currencies→ daftar semua currency yang didukungSemua data diambil sekali saat startup via ApplicationRunner dan disimpan
di in-memory store yang thread-safe dan immutable.
Spread Factor
USD_BuySpread_IDR = (1 / Rate_USD) * (1 + 0.00191)Architectural Rationale
1. Mengapa Strategy Pattern?
Strategy Pattern dipilih dibandingkan blok
if/elseatauswitchbiasadengan alasan berikut:
Ekstensibilitas: Menambahkan resource type baru hanya membutuhkan pembuatan
class baru yang mengimplementasikan interface
IDRDataFetcherdan diberianotasi
@Component. Tidak ada kode yang sudah ada yang perlu dimodifikasi,mengikuti prinsip Open/Closed.
Kemudahan Perawatan: Setiap class fetcher memiliki satu tanggung jawab.
LatestIDRRatesFetcherhanya menangani logika rates terbaru,HistoricalIDRUsdFetcherhanya menangani data historis, dan seterusnya.Hal ini membuat kode lebih mudah dibaca, diuji, dan di-debug secara independen.
Tidak Ada Logika Kondisional di Controller: Spring secara otomatis
meng-inject semua bean
IDRDataFetcherke dalamMapdengan key darigetResourceType(). Controller cukup melakukan map lookup tanpaif/elseatau
switchsama sekali.2. Mengapa FactoryBean dibanding @bean biasa?
Enkapsulasi Logika Konstruksi yang Kompleks:
WebClientFactoryBeanmengenkapsulasi semua detail konstruksi WebClient seperti konfigurasi timeout,
penanganan redirect, filter logging, dan injeksi base URL dalam satu class
yang kohesif.
Desain OOP yang Lebih Baik:
FactoryBeanadalah sebuah interface, artinyafactory itu sendiri adalah Spring-managed bean dengan lifecycle-nya sendiri.
Dapat diuji secara independen, di-subclass, atau diganti tanpa menyentuh
konfigurasi lain.
Pemisahan Concern: Configuration class seharusnya mendeklarasikan bean apa
yang ada, bukan bagaimana objek kompleks dibangun.
FactoryBeanmemisahkanconcern "cara membangun" ke dalam class tersendiri sesuai prinsip
Single Responsibility.
3. Mengapa ApplicationRunner dibanding @PostConstruct?
ApplicationRunner berjalan setelah ApplicationContext sepenuhnya siap:
@PostConstructberjalan saat inisialisasi bean, sebelum context sepenuhnyadimulai. Artinya bean lain seperti WebClient dan properties mungkin belum
sepenuhnya terinisialisasi, berisiko menyebabkan
NullPointerException.Penanganan Error yang Lebih Baik: Jika
ApplicationRunner.run()melemparexception, Spring Boot akan menghentikan startup secara graceful dan mencatat
pesan kegagalan yang jelas.
Pemisahan yang Lebih Bersih:
ApplicationRunneradalah komponenberdedikasi dengan satu tujuan yang jelas yaitu menjalankan logika setelah
startup.
@PostConstructmencampur logika inisialisasi ke dalam lifecyclemethod bean yang lebih sulit diuji dan dipahami.