Skip to content

Feature/idr rate aggregator test#214

Open
furqonfajri wants to merge 10 commits into
allobankdev:mainfrom
furqonfajri:feature/idr-rate-aggregator
Open

Feature/idr rate aggregator test#214
furqonfajri wants to merge 10 commits into
allobankdev:mainfrom
furqonfajri:feature/idr-rate-aggregator

Conversation

@furqonfajri
Copy link
Copy Markdown

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_IDR
  • historical_idr_usd → data historis IDR/USD periode 2024-01-01 s/d 2024-01-05
  • supported_currencies → daftar semua currency yang didukung

Semua data diambil sekali saat startup via ApplicationRunner dan disimpan
di in-memory store yang thread-safe dan immutable.


Spread Factor

  • GitHub Username: furqonfajri
  • ASCII Sum: f(102)+u(117)+r(114)+q(113)+o(111)+n(110)+f(102)+a(97)+j(106)+r(114)+i(105) = 1191
  • Spread Factor: (1191 % 1000) / 100000.0 = 0.00191
  • Formula: USD_BuySpread_IDR = (1 / Rate_USD) * (1 + 0.00191)

Architectural Rationale

1. Mengapa Strategy Pattern?

Strategy Pattern dipilih dibandingkan blok if/else atau switch biasa
dengan alasan berikut:

Ekstensibilitas: Menambahkan resource type baru hanya membutuhkan pembuatan
class baru yang mengimplementasikan interface IDRDataFetcher dan diberi
anotasi @Component. Tidak ada kode yang sudah ada yang perlu dimodifikasi,
mengikuti prinsip Open/Closed.

Kemudahan Perawatan: Setiap class fetcher memiliki satu tanggung jawab.
LatestIDRRatesFetcher hanya menangani logika rates terbaru,
HistoricalIDRUsdFetcher hanya 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 IDRDataFetcher ke dalam Map dengan key dari
getResourceType(). Controller cukup melakukan map lookup tanpa if/else
atau switch sama sekali.


2. Mengapa FactoryBean dibanding @bean biasa?

Enkapsulasi Logika Konstruksi yang Kompleks: WebClientFactoryBean
mengenkapsulasi 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: FactoryBean adalah sebuah interface, artinya
factory 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. FactoryBean memisahkan
concern "cara membangun" ke dalam class tersendiri sesuai prinsip
Single Responsibility.


3. Mengapa ApplicationRunner dibanding @PostConstruct?

ApplicationRunner berjalan setelah ApplicationContext sepenuhnya siap:
@PostConstruct berjalan saat inisialisasi bean, sebelum context sepenuhnya
dimulai. Artinya bean lain seperti WebClient dan properties mungkin belum
sepenuhnya terinisialisasi, berisiko menyebabkan NullPointerException.

Penanganan Error yang Lebih Baik: Jika ApplicationRunner.run() melempar
exception, Spring Boot akan menghentikan startup secara graceful dan mencatat
pesan kegagalan yang jelas.

Pemisahan yang Lebih Bersih: ApplicationRunner adalah komponen
berdedikasi dengan satu tujuan yang jelas yaitu menjalankan logika setelah
startup. @PostConstruct mencampur logika inisialisasi ke dalam lifecycle
method bean yang lebih sulit diuji dan dipahami.

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