Tugas Besar IF4031 — Milestone 1: Sistem Terdistribusi Dasar

Soal / Brief

Konteks Umum Tugas Besar

Tugas besar IF4031 (Arsitektur Aplikasi Terdistribusi) terdiri dari dua milestone yang berkelanjutan — sistem yang dibangun di M1 menjadi fondasi yang diskalakan di M2. Asumsi dan keputusan desain di M1 akan terus terbawa dan diuji ulang di M2.

Skenario cerita (tim Astolfo): membangun platform koordinasi kebencanaan yang menjembatani BMKG (gempa/tsunami), PVMBG (gunung api), dan BNPB (koordinasi), karena ketiga instansi ini punya sistem otonom sendiri-sendiri, berbeda instrumen dan bahasa data, yang tidak saling terhubung — menyebabkan keterlambatan/ketidaksinkronan informasi saat krisis.

TahapanDeadline
M1 — Sistem Terdistribusi DasarJumat, 9 Oktober 2026, 23:59 WIB
M2 — Sistem Terdistribusi ScalableMinggu, 20 November 2026, 23:59 WIB

Ketentuan umum lintas milestone:

  1. Deadline bersifat HARD DEADLINE.
  2. Laporan wajib dibuat per milestone (ketentuan di dokumen spesifikasi masing-masing).
  3. Penilaian dilakukan per pengumpulan milestone — kode wajib bisa dijalankan + README lengkap.
  4. Kelompok terdiri atas 3 anggota (4 anggota hanya kalau jumlah kelas tidak habis dibagi 3, dan kelompok lain sudah penuh 3 orang). Isi kelompok di sheets sebelum Minggu, 19 September 2026, 23:59.
  5. LLM boleh dipakai untuk rancangan maupun implementasi, wajib dideklarasikan di laporan, kode yang dihasilkan wajib dipahami sepenuhnya, dan wajib melampirkan Form Penggunaan AI di laporan. Kesamaan dengan pekerjaan lain/repo internet = kecurangan, alasan LLM tidak diterima.

Milestone 1: Sistem Terdistribusi Dasar

Seluruh problem M1 dikerjakan di atas satu sistem yang sama: platform koordinasi kebencanaan BMKG–PVMBG–BNPB. Cakupan dibatasi pada dua instansi sumber (BMKG, PVMBG) dan dua jenis bencana (seismik, vulkanik), dengan BNPB sebagai node koordinasi.

Tiap problem dinilai dari dua sisi:

  • Laporan: desain, alasan keputusan, dokumentasi.
  • Implementasi: proof-of-concept yang benar-benar menyelesaikan masalah (bukan cuma dijelaskan).

Ketentuan Implementasi (berlaku sejak problem pertama)

  1. Komunikasi antar BMKG/PVMBG/BNPB/client wajib lewat panggilan jaringan (REST, gRPC, atau message broker). Dilarang panggil fungsi langsung / berbagi library business-logic lintas service.
  2. Setiap storage dimiliki tepat satu service. Service lain wajib minta lewat API pemilik, bukan connect langsung ke storage yang sama. Berlaku juga untuk Canonical Store: dimiliki Aggregator, Client-Facing API baca lewat API Aggregator.
  3. Setiap service wajib jadi container Docker sendiri (Dockerfile masing-masing), seluruh sistem nyala lewat satu perintah orkestrasi (mis. docker compose up).
  4. Setiap service wajib bisa nyala/mati independen, diuji dengan kegagalan/keterlambatan yang benar-benar dipicu saat demo (bukan diasumsikan/dinarasikan saja).
  5. Kredensial satu instansi tidak berlaku di instansi lain — solusi kredensial tunggal dianggap tidak menyelesaikan persoalan.
  6. Kredensial/signing key/config antar-environment wajib dari environment variable/config file yang tidak ikut commit. Repo wajib punya .env.example. Hardcode/ter-commit mengurangi nilai.
  7. Setiap service wajib punya health check endpoint, log terstruktur dengan correlation ID yang diteruskan lintas service dalam satu alur request, serta mencatat latensi tiap panggilan keluar.

Model Ingest

Gateway BNPB polling berkala ke kedua mock (mock pasif, tidak push).

KetentuanNilai
Pola ingestPolling dari BNPB ke mock (mock tidak push)
Interval pollingConfigurable, disarankan 2–5 detik
Laju data baru di mockMinimal 1 event baru/10 detik/instansi (bisa dipercepat untuk demo)
Data awalTiap mock wajib seed minimal 20 record historis saat dinyalakan
PortBMKG 8081, PVMBG 8082, BNPB Client-Facing API 8080 (bisa diubah via config, wajib didokumentasikan di README)

Endpoint minimum wajib pada mock:

InstansiEndpointKeterangan
BMKGGET /seismic-events?since=<timestamp>Mengembalikan SeismicEvent sejak waktu tertentu
BMKGGET /tsunami-warnings?since=<timestamp>Terpisah; korelasi ke SeismicEvent lewat related_event_id dilakukan Aggregator BNPB
BMKGGET /healthHealth check
PVMBGGET /volcanic-reports?since=<timestamp>Mengembalikan VolcanicReport sejak waktu tertentu
PVMBGPOST /admin/schema-versionMengaktifkan/menonaktifkan kemunculan confidence_level saat sistem berjalan
PVMBGPOST /admin/outageMenyalakan/mematikan kondisi outage secara instan
PVMBGGET /healthHealth check

Kedua mock wajib memvalidasi kredensial masuk, mengembalikan 401/403 kalau tidak sesuai/berasal dari instansi lain. Mock yang menerima kredensial apa pun bikin Problem 3 tidak bisa dinilai.

Kontrak BMKG (Simulasi)

Domain: pemantauan gempa dan potensi tsunami. Entitas SeismicEvent dan TsunamiWarning (TsunamiWarning hanya untuk event dengan potential_tsunami = true, diambil lewat endpoint terpisah).

Response time rendah & konsisten (delay tetap 50–150 ms). Akses pakai API key sendiri lewat header X-BMKG-Key.

SeismicEvent

FieldTipeKeterangan
event_idstringIdentifier unik
magnitudefloatSkala Mw
depth_kmfloatKedalaman episentrum
epicenter_lat, epicenter_lonfloatKoordinat episentrum
region_namestringNama wilayah terdampak
occurred_atdatetime (ISO 8601)Waktu kejadian
potential_tsunamibooleanPenanda potensi tsunami

TsunamiWarning

FieldTipeKeterangan
warning_idstringIdentifier unik
related_event_idstringReferensi ke SeismicEvent
threat_levelenum (Waspada/Siaga/Awas)Tingkat ancaman
affected_zoneslist<string>Daftar wilayah terdampak
estimated_arrivaldatetimeEstimasi waktu tiba gelombang

Kontrak PVMBG (Simulasi)

Domain: pemantauan gunung api.

  • Response time lebih tinggi & fluktuatif dari BMKG (delay acak 500 ms–3 detik, simulasi infrastruktur lawas). Besaran delay wajib configurable.
  • Wajib mekanisme outage on/off instan lewat POST /admin/outage. Saat outage, PVMBG tidak merespons sama sekali / mengembalikan galat. Durasi dikendalikan penuh operator demo (skenario “mati 10–20 menit” bisa ditunjukkan).

Akses PVMBG pakai kredensial terpisah dari BMKG, format berbeda: header Authorization: Bearer <pvmbg-token> dengan struktur token tersendiri.

VolcanicReport

FieldTipeKeterangan
report_idstringIdentifier unik
volcano_idstringIdentifier gunung api
alert_levelenum (Normal/Waspada/Siaga/Awas)Tingkat aktivitas
eruption_count_24hintegerJumlah letusan dalam 24 jam terakhir
ash_column_height_mfloatTinggi kolom abu (meter)
reported_atdatetime (ISO 8601)Waktu laporan
confidence_level (field baru)float (0–1)Muncul di tengah masa berjalan, lihat catatan di bawah

Skenario khusus: confidence_level tidak ada saat sistem pertama nyala, mulai muncul setelah POST /admin/schema-version dipanggil tanpa mock/gateway di-restart. Urutan wajib didemokan: sistem nyala → gateway normal dengan skema lama → skema diubah → gateway tetap jalan tanpa restart.

Format Kanonik

Representasi tunggal yang dipakai BNPB untuk menyatukan data dari kedua instansi. Seluruh problem yang menyebut “data kanonik”/“event kanonik” merujuk ke entitas ini.

Entitas HazardEvent:

FieldTipeKlasifikasiKeterangan
hazard_idstringRingkasanIdentifier kanonik yang dibangkitkan BNPB
sourceenum (BMKG/PVMBG)RingkasanInstansi asal data
source_ref_idstringMentahIdentifier asli (event_id/report_id)
hazard_typeenum (SEISMIC/VOLCANIC)RingkasanJenis bahaya
severityenum (NORMAL/WASPADA/SIAGA/AWAS)RingkasanTingkat ancaman terpadu
area_namestringRingkasanNama wilayah/gunung api terdampak
latitude, longitudefloatMentahKoordinat presisi
occurred_atdatetime (ISO 8601)RingkasanWaktu kejadian di sumber
ingested_atdatetime (ISO 8601)RingkasanWaktu data diterima BNPB
attributesmap<string, any>MentahAtribut spesifik sumber tanpa padanan kanonik

Pemetaan wajib SeismicEvent → HazardEvent:

KanonikAsal
source_ref_idevent_id
hazard_typeSEISMIC
area_nameregion_name
latitude, longitudeepicenter_lat, epicenter_lon
occurred_atoccurred_at
severityAWAS bila ada TsunamiWarning dengan threat_level=Awas; selain itu ikuti threat_level TsunamiWarning terkait; bila tidak ada warning: NORMAL untuk magnitude < 5.0, WASPADA untuk 5.0 ≤ magnitude < 6.5, SIAGA untuk magnitude ≥ 6.5
attributesmagnitude, depth_km, potential_tsunami, + field TsunamiWarning terkait bila ada

Pemetaan VolcanicReport → HazardEvent:

KanonikAsal
source_ref_idreport_id
hazard_typeVOLCANIC
area_nameNama gunung api hasil pemetaan dari volcano_id
latitude, longitudeKoordinat gunung api dari tabel referensi statis milik BNPB
occurred_atreported_at
severityalert_level dipetakan langsung ke enum kanonik
attributeseruption_count_24h, ash_column_height_m, + confidence_level bila tersedia

Field yang tidak bisa dipetakan/tidak ada padanan kanonik (termasuk field baru seperti confidence_level) masuk ke attributes.

Kontrak BNPB (Komponen yang Dibangun Kelompok)

BNPB adalah objek utama yang dinilai.

KomponenFungsiTerkait Problem
Gateway / AggregatorPolling BMKG & PVMBG, memetakan ke HazardEvent, publish eventP1, P2, P5
Canonical StoreMenyimpan HazardEvent; dimiliki & hanya diakses langsung AggregatorP1, P4
Client-Facing APIEndpoint untuk client downstreamP2, P3
Auth / Token ServiceMenerbitkan token ke client downstream dengan scope berbedaP3
Message BrokerMenyalurkan HazardEvent dari producer ke banyak consumerP5
Event Consumer (≥2)Berlangganan HazardEvent (mis. dashboard updater, notifier)P5

Client Downstream & Hak Akses

ClientHak AksesCatatan
Media / PersHanya field klasifikasi RingkasanKebocoran field mentah (koordinat presisi, attributes) = gagal
Tim Lapangan (BASARNAS/Pemda)Seluruh field termasuk MentahAccess token ber-TTL pendek; wajib ada alur refresh tanpa login ulang manual
BNPB Pusat / Internal OpsSeluruh field termasuk MentahBaseline hak akses tertinggi

Requirements:

  1. Ketiga client wajib identitas & kredensial benar-benar berbeda (bukan satu akun + flag role). Pembeda hak akses ditegakkan di sisi BNPB.
  2. Access token TTL pendek & configurable, default 60 detik.
  3. Di M1, seluruh client read-only. Jalur pelaporan lapangan → BNPB di luar cakupan M1.

Problem Set

Problem 1 — Arsitektur & Interoperabilitas Data

Cerita: Gateway Astolfo di-hardcode terhadap skema SeismicEvent/VolcanicReport yang diasumsikan tetap. PVMBG menambahkan confidence_level tanpa koordinasi → gateway gagal parsing / field hilang.

Masalah inti: Gateway hardcode ke bentuk skema tetap → perubahan skema aditif dari instansi manapun memaksa perubahan kode gateway + redeploy, padahal instansi sumber tidak berkoordinasi soal kapan mereka boleh mengubah skema.

Kriteria penyelesaian:

  1. Jalankan sistem dengan PVMBG skema awal, tunjukkan Aggregator hasilkan HazardEvent sesuai aturan pemetaan untuk kedua sumber.
  2. Picu perubahan skema PVMBG via POST /admin/schema-version saat sistem berjalan, tanpa restart/redeploy service manapun.
  3. Tunjukkan Aggregator tetap hasilkan HazardEvent valid setelah perubahan, tanpa crash, tanpa intervensi manual pada kode.
  4. Deklarasikan kebijakan penanganan field tak dikenal (diteruskan ke attributes atau diabaikan), tunjukkan perilaku konsisten dengan kebijakan tersebut. Yang dinilai: konsistensi terhadap kebijakan, bukan pilihan kebijakannya.
  5. Di laporan: jelaskan mekanisme (schema registry, dynamic field mapping, schema evolution ala Protobuf/Avro, atau JSON additive-only policy), nyatakan eksplisit jenis perubahan skema yang didukung vs sengaja tidak didukung.

Problem 2 — Concurrency, Availability, & Graceful Degradation

Cerita: Gateway panggil BMKG lalu PVMBG berurutan dalam satu alur → request yang cuma butuh data BMKG ikut menunggu PVMBG yang lambat. Saat status siaga + banyak akses bersamaan → sistem jadi tidak responsif. PVMBG mati → seluruh dashboard BNPB ikut kosong padahal data BMKG masih normal & data vulkanik terakhir masih tersimpan.

Masalah inti (3 kegagalan):

  • Head-of-line blocking: panggilan sekuensial BMKG+PVMBG bikin request cepat tertahan request lambat yang tidak berhubungan.
  • Resource exhaustion: tidak ada batas concurrent request → lonjakan akses menghabiskan resource hingga sistem berhenti melayani semua pihak.
  • Ketiadaan degradasi terkendali: matinya satu sumber bikin seluruh respons gagal, padahal sebagian data masih tersedia/berguna.

Kriteria penyelesaian:

  1. Delay PVMBG = 3 detik, kirim request BMKG-only bersamaan dengan request PVMBG. Tunjukkan p95 latensi request BMKG-only tetap < 300 ms.
  2. Load testing ≥50 koneksi paralel, minimal 60 detik, pola sustained. Laporkan throughput, p50/p95/p99, error rate. Sistem wajib tidak crash, error rate di luar penolakan terkontrol < 1% (HTTP 429 tidak dihitung gagal, selama dilaporkan).
  3. Nyalakan outage PVMBG via POST /admin/outage. Selama outage: request data seismik tetap normal; request data vulkanik dijawab data terakhir dari Canonical Store + penanda kebaruan (mis. stale_since) atau status ketidaktersediaan eksplisit (bukan 500, bukan hang sampai timeout client). Setelah outage mati, sistem normal kembali tanpa restart.
  4. Di laporan: jelaskan model konkurensi yang dipilih & bagaimana mencegah ketiga kegagalan di atas, sertakan konfigurasi load testing + tooling.

Problem 3 — Service-to-Service Authentication & Trust

Cerita: Versi awal pakai satu kunci rahasia untuk akses BMKG maupun PVMBG, sekaligus dipakai ulang sebagai kunci akses client eksternal → kalau kunci media bocor, mereka bisa lihat data mentah juga.

Masalah inti: Tidak ada pemisahan domain of trust. Kredensial BMKG, kredensial PVMBG, dan token client downstream seharusnya independen — kompromi satu domain tidak boleh berdampak ke domain lain.

Kriteria penyelesaian:

  1. Kredensial valid BMKG ditolak 401/403 saat dipakai akses PVMBG, dan sebaliknya.
  2. Client scope Media hanya terima field Ringkasan. Request eksplisit ke field mentah ditolak server 401/403 (bukan cuma disembunyikan di tampilan).
  3. Tunggu access token Tim Lapangan expired alami sesuai TTL (default 60 detik) di tengah sesi aktif. Tunjukkan alur refresh hasilkan token baru tanpa login ulang manual, token lama ditolak setelah refresh.
  4. Tunjukkan tidak ada kredensial hardcode/ter-commit, .env.example tersedia.
  5. Di laporan: jelaskan mekanisme yang dipakai + alasan kenapa tidak membuka celah baru.

Problem 4 — Arsitektur Microservice & Flexible Storage

Cerita: Gateway sudah benar secara fungsi tapi semua logic (polling, pemetaan skema, autentikasi, penyajian client) hidup dalam satu unit deploy → perbaikan kecil di autentikasi memaksa seluruh sistem redeploy. Storage: HazardEvent disimpan di tabel relasional kolom tetap → tiap field baru (mis. confidence_level) butuh migrasi + koordinasi deploy bersama.

Masalah inti (2 hal berkaitan):

  • Sistem belum berbentuk kumpulan service independen — ubah satu bagian memaksa redeploy bagian lain yang tidak berhubungan.
  • Mekanisme penyimpanan tidak sejalan dengan sifat data yang skemanya bisa berubah sewaktu-waktu → masalah coupling Problem 1 muncul lagi lewat jalur storage.

Kriteria penyelesaian:

  1. Tunjukkan tiap komponen jalan sebagai container terpisah; salah satunya bisa dihentikan/di-rebuild/dijalankan ulang tanpa rebuild/restart container lain. Sistem tetap melayani request yang tidak bergantung pada komponen yang sedang restart.
  2. Simpan HazardEvent di storage yang akomodasi atribut dinamis tanpa migrasi skema saat field baru muncul (document store/key-value/relasional dgn kolom dokumen JSONB — bebas, syarat: tidak ada langkah migrasi saat confidence_level muncul). Tunjukkan record sebelum & sesudah perubahan skema tersimpan berdampingan, keduanya bisa dibaca.
  3. Tunjukkan Canonical Store hanya diakses langsung Aggregator; komponen lain lewat API Aggregator, sesuai poin (2).
  4. Di laporan: alasan penentuan batas tiap service (kenapa dipecah begitu, bukan pembagian lain), bandingkan minimal dua alternatif penyimpanan + konsekuensi pilihan.

Problem 5 — Fan-out Event Distribution/Pub-sub

Cerita: Makin banyak pihak ingin tahu tiap ada data baru (dashboard internal, notifikasi tim lapangan, rencana portal pemda). Kalau Aggregator panggil tiap pihak satu-satu secara langsung, satu pihak lambat bisa menahan notifikasi ke tim lapangan yang mendesak. Tiap ada subscriber baru, kode Aggregator harus diubah lagi.

Masalah inti: Producer & consumer saling terikat langsung (tight coupling) — producer harus tahu & panggil tiap consumer secara sinkron; kelambatan/kegagalan satu consumer berdampak ke producer & consumer lain; consumer baru = ubah kode producer.

Kriteria penyelesaian:

  1. Tiap HazardEvent baru hasil pemetaan dipublikasikan ke message broker (Kafka/ RabbitMQ/NATS/dst.), bukan dikirim langsung ke tiap consumer.
  2. Tunjukkan minimal dua consumer independen berlangganan stream yang sama, terima event tanpa producer tahu keberadaan mereka.
  3. Hentikan salah satu consumer sementara. Tunjukkan producer tetap publish tanpa tertahan, consumer lain tetap terima event normal. Setelah consumer nyala lagi, jelaskan+tunjukkan nasib event yang terbit selama consumer mati (tersusul/hilang sesuai konfigurasi broker).
  4. Tunjukkan penambahan satu consumer baru pada topic yang sudah ada, tanpa mengubah kode producer sama sekali.
  5. Di laporan: broker & pola yang dipilih, semantik pengiriman (at-least-once/ at-most-once), konsekuensi bagi consumer (perlu idempoten atau tidak, cara menyikapinya).

Deliverables

  1. Laporan PDF sesuai ketentuan di bawah.
  2. Repositori kode: seluruh mock, seluruh komponen BNPB, orchestration files, .env.example, README.

Laporan

Format nama file: IF4031_M1_<NamaKelompok>.pdf

Bagian Umum:

  1. Halaman sampul (nama kelompok, nama+NIM anggota, nama sistem, identitas matkul).
  2. Tabel kontribusi tiap anggota (perancangan, implementasi, penulisan laporan).
  3. Deskripsi umum sistem + lingkup M1.
  4. Diagram arsitektur sistem (seluruh service, protokol komunikasi, kepemilikan penyimpanan, alur event, batas container) + penjelasan naratif.
  5. Daftar teknologi terpilih per komponen + alasan pemilihan dikaitkan kebutuhan sistem.
  6. Asumsi (perancangan & implementasi).
  7. Petunjuk menjalankan sistem (ringkasan, merujuk README).

Bagian Per Problem (P1–P5), masing-masing:

  1. Rancangan solusi & mekanisme yang dipilih.
  2. Alasan keputusan: alternatif dipertimbangkan + trade-off.
  3. Batasan yang disadari (hal yang sengaja tidak ditangani + alasan).
  4. Bukti penyelesaian (screenshot, log ber-correlation ID, hasil pengukuran, output load testing) yang menunjukkan tiap kriteria terpenuhi.
  5. Jawaban butir “Di laporan” pada problem terkait.

Ketentuan penulisan: tiap klaim harus tertelusuri ke bukti (dokumentasi/kode di repo). LLM boleh dipakai dengan deklarasi eksplisit + kode dipahami penuh. Kesamaan dengan kelompok lain/repo internet = kecurangan.

Demonstrasi

Sinkron, setelah tenggat M1. Detail diberikan paling lambat H+1 dari tenggat M1.

Pengumpulan

Sebelum Jumat, 9 Oktober 2026, 23:59 WIB (hard deadline).

Mekanisme:

  1. Repo GitHub private, jadi public setelah deadline.
  2. Buat tag milestone-1 pada commit terakhir yang dinilai. Commit setelah tenggat tidak dinilai.
  3. Isi Formulir pengumpulan sekali per kelompok: nama kelompok, NIM pengumpul, tautan repo, tautan laporan PDF.
  4. Nama file laporan: IF4031_M1_<NamaKelompok>.pdf.

README wajib memuat: langkah menjalankan sistem dari kondisi awal, daftar variabel konfigurasi, cara memicu tiap skenario pengujian. Kode yang tidak bisa dijalankan mengikuti README hanya dinilai seadanya.

Asistensi diperbolehkan, koordinasi langsung dengan asisten.

Penilaian

KomponenPoin
Problem Set (5 problem × 18 poin)90
Demonstrasi10
Total100

Ketentuan / Yang Dikumpulkan

  • Kelompok terdaftar di sheets pemilihan kelompok (3 anggota, deadline pengisian Minggu 2026-09-19 23:59)
  • Problem 1 — schema evolution (skenario POST /admin/schema-version tanpa restart)
  • Problem 2 — concurrency, load testing ≥50 koneksi/60 detik, graceful degradation saat PVMBG outage
  • Problem 3 — auth terpisah BMKG/PVMBG/client (Media/Tim Lapangan/Internal Ops), token TTL + refresh flow
  • Problem 4 — tiap service container terpisah + independent restart, storage schemaless untuk HazardEvent
  • Problem 5 — message broker fan-out, ≥2 consumer independen, tambah consumer tanpa ubah kode producer
  • Seluruh service via Docker + satu perintah orkestrasi (docker compose up)
  • .env.example tersedia, tidak ada kredensial hardcode/ter-commit
  • Health check endpoint + structured log dengan correlation ID di tiap service
  • README lengkap (cara jalanin dari awal, variabel config, cara trigger tiap skenario)
  • Laporan PDF IF4031_M1_<NamaKelompok>.pdf (sampul, kontribusi, deskripsi sistem, diagram arsitektur, teknologi, asumsi, + bagian per problem P1–P5)
  • Form Penggunaan AI dilampirkan di laporan (jika pakai LLM)
  • Repo GitHub private → public setelah deadline, tag milestone-1
  • Formulir pengumpulan diisi (nama kelompok, NIM pengumpul, link repo, link laporan)

Progress & Catatan Pengerjaan

Rancangan Solusi per Problem (draf awal)

Problem 1 — Schema Evolution

Solusi dipilih: Tolerant reader + dynamic field mapping (parse response PVMBG sebagai map/dict, bukan struct kaku), dengan kebijakan additive-only: field yang dikenal dipetakan sesuai kontrak kanonik, field tak dikenal diteruskan ke attributes (bukan diabaikan — supaya tidak kehilangan informasi, dan lebih gampang didemokan “data tetap ada, cuma masuk attributes”).

Alasan: paling ringan diimplementasikan (tidak butuh infra tambahan), langsung cocok dengan sifat REST/JSON yang dipakai mock, dan selaras dengan pilihan storage schemaless di Problem 4 (field baru otomatis ikut tersimpan di attributes tanpa kerja tambahan).

Alternatif lain:

  • Schema registry + Avro/Protobuf (skema evolution formal ala Kafka ecosystem) — lebih robust untuk production, tapi butuh infra tambahan (registry service) dan cocoknya dengan komunikasi biner/gRPC, bukan REST JSON seperti kontrak mock. Bisa dipilih kalau kelompok memang membangun Aggregator↔mock dengan gRPC.
  • Endpoint bervarian versi (/v1/... vs /v2/...) — tidak cocok dipakai di sini karena kontrak mock sudah fix: endpoint sama, skema berubah lewat toggle /admin/schema-version, bukan lewat path baru.
  • Strict schema validation, tolak field asing — pendekatan berlawanan, sengaja tidak dipilih karena kriteria penilaian eksplisit minta gateway tetap jalan saat field baru muncul, bukan menolaknya.

Problem 2 — Concurrency & Graceful Degradation

Solusi dipilih: kombinasi tiga pola —

  1. Panggilan ke BMKG & PVMBG dilakukan paralel/async (bukan sekuensial) dengan connection pool terpisah per sumber (bulkhead), supaya lambatnya PVMBG tidak memblokir request yang cuma butuh BMKG.
  2. Concurrency limiter / rate limiter (semaphore atau reverse proxy dengan max_conns) di Client-Facing API, menolak kelebihan beban dengan HTTP 429 (penolakan terkontrol) daripada membiarkan resource habis.
  3. Circuit breaker pada panggilan ke PVMBG: begitu terdeteksi outage/gagal beruntun, breaker terbuka, Aggregator langsung sajikan data terakhir dari Canonical Store
    • stale_since, tanpa menunggu timeout.

Alasan: tiga pola ini masing-masing menjawab satu dari tiga kegagalan yang disebutkan di soal (HOL blocking, resource exhaustion, tidak ada degradasi terkendali), dan ini kombinasi standar (mirip resilience pattern ala Hystrix/Polly) yang gampang dipetakan langsung ke kriteria penilaian (p95 latency, error rate load test, perilaku saat outage).

Alternatif lain:

  • Queue-based load leveling (semua request masuk antrean, diproses worker pool tetap) — meredam lonjakan, tapi menambah latensi ke semua request termasuk yang harusnya cepat (BMKG-only), jadi kurang pas untuk kriteria p95 < 300ms. Bisa jadi pelengkap, bukan solusi utama.
  • Cukup perbesar thread/connection pool tanpa isolasi — paling gampang diimplementasi, tapi cuma menunda masalah resource exhaustion (bukan menyelesaikannya), berisiko tetap gagal di load test ≥50 koneksi sustained. Sengaja tidak dipilih.
  • Local cache PVMBG dengan TTL pendek di Aggregator — bisa dipakai bareng circuit breaker (bukan pengganti) untuk mengurangi ketergantungan ke call live tiap request; worth ditambahkan kalau waktu ada.

Problem 3 — Service-to-Service Auth & Trust

Solusi dipilih: kredensial BMKG (API key X-BMKG-Key) dan PVMBG (Bearer token format sendiri) disimpan sebagai secret independen di Aggregator, tidak pernah di-share. Untuk client downstream: Auth/Token Service menerbitkan JWT ber-scope (media / lapangan / internal), TTL pendek default 60 detik (configurable), Client-Facing API validasi signature + cek klaim scope terhadap klasifikasi field (Ringkasan/Mentah) sebelum mengembalikan response. Refresh token terpisah (lebih panjang umur) untuk memperbarui access token tanpa login ulang.

Alasan: JWT stateless (tidak butuh session store buat validasi tiap request), scope-based check menegakkan hak akses di sisi server (bukan asumsi client), refresh flow memenuhi kriteria “tanpa login ulang manual”, dan ini pola standar industri yang gampang dijelaskan alasannya di laporan.

Alternatif lain:

  • mTLS antar service + OAuth2 untuk client — secara keamanan lebih kuat (identitas di layer transport), tapi berat di ops (issued cert per service, rotasi, ribet di Docker Compose lokal). Masuk akal untuk production, tapi berlebihan untuk skala tugas.
  • Opaque token + session store (Redis) — kelebihannya revocation instan (hapus session langsung membatalkan token), sementara JWT baru “mati” saat TTL habis. Ini alternatif yang sama validnya, terutama kalau kelompok mau demo “kredensial dicabut instan” — kalau itu jadi concern, worth dipertimbangkan ulang.
  • Auth lewat API Gateway pihak ketiga (Kong/Envoy plugin OAuth2) — mengurangi kode custom, tapi menambah komponen infra baru untuk dipelajari/di-container-kan, padahal spek eksplisit minta komponen “Auth/Token Service” sendiri di diagram arsitektur — bikin custom lebih pas untuk penilaian deliverable.

Problem 4 — Microservice Boundaries & Flexible Storage

Solusi dipilih: pisah jadi service independen sesuai tabel komponen (Aggregator, Client-Facing API, Auth/Token Service, masing-masing container+Dockerfile sendiri, komunikasi via REST/API internal). Canonical Store pakai MongoDB (document store) — dipilih karena HazardEvent.attributes memang punya bentuk dinamis, index di hazard_id/occurred_at/hazard_type cukup untuk query since=, dan tidak butuh migrasi apa pun saat confidence_level muncul.

Alasan: batas service mengikuti tabel komponen resmi di soal (tiap komponen = satu tanggung jawab dan satu alasan untuk berubah), storage document-native paling langsung memenuhi kriteria “tanpa migrasi skema”.

Alternatif lain (worth dipertimbangkan serius):

  • PostgreSQL + kolom JSONB untuk attributes, field lain (hazard_id, severity, dst.) tetap kolom relasional biasa — dapat query relasional/ACID untuk field yang memang stabil, plus fleksibilitas JSONB untuk yang dinamis. Ini alternatif yang legitimately sama kuat, bahkan bisa lebih pas karena mayoritas field HazardEvent memang tetap (cuma attributes yang berubah-ubah) — pilih ini kalau tim lebih familiar SQL atau butuh join/transaksi di M2 nanti.
  • Key-value store (DynamoDB/Redis) — sengaja tidak dipilih: kurang mendukung range query by time / filter by severity yang dibutuhkan Client-Facing API tanpa index sekunder tambahan (infra ekstra yang tidak sepadan untuk skala tugas).
  • Event sourcing (simpan payload mentah append-only, hitung canonical view saat dibaca) — secara arsitektur lebih “murni” untuk data yang skemanya berevolusi + dapat audit trail gratis, tapi kompleksitas implementasinya besar untuk waktu M1. Menarik untuk dieksplorasi di M2 (skalabilitas), tidak untuk M1.

Problem 5 — Fan-out Pub/Sub

Solusi dipilih: RabbitMQ, exchange tipe fanout/topic bernama hazard-events, tiap consumer declare queue durable miliknya sendiri dan di-bind ke exchange itu (bukan satu queue dibagi rame-rame — itu jadi competing consumers/load balancing, bukan fan-out, dan bakal gagal kriteria “≥2 consumer terima event yang sama”).

Alasan: untuk scope tugas ini, setup RabbitMQ lebih ringan resource (1 container, tidak perlu Zookeeper/KRaft) dan lebih simpel dikonfigurasi di Docker Compose dibanding Kafka (Kafka sering menyulitkan lewat advertised.listeners yang harus diatur biar bisa diakses dari dalam maupun luar Docker network). Semua kriteria tetap terpenuhi: fan-out ke ≥2 consumer independen (fanout/topic exchange), event tetap publish walau satu consumer mati (queue durable lain tidak terpengaruh), event tersusul setelah consumer nyala lagi (queue durable + pesan persistent menampung selama consumer offline), dan consumer baru bisa ditambah cukup dengan declare queue+binding baru tanpa menyentuh kode producer. Kelemahan dibanding Kafka: consumer baru yang baru pertama kali dibuat tidak otomatis dapat riwayat pesan sebelum queue-nya ada (beda dari Kafka yang bisa rewind ke retention lama) — tapi ini di luar cakupan kriteria 4 (kriteria cuma minta consumer baru terima event ke depannya, bukan backfill riwayat).

Alternatif lain:

  • Kafka (consumer group + offset tracking per topic) — defaultnya memang fan-out murni (lebih susah salah pola dibanding RabbitMQ yang harus sengaja pilih exchange fanout/topic), dan retensi log-nya bikin consumer baru pun bisa rewind ke riwayat lama, bukan cuma event ke depan. Trade-off: JVM-based, minimal 512MB–1GB RAM per proses, mode klasik butuh 2 proses (Zookeeper + broker) — mode KRaft (tanpa Zookeeper) mengurangi ini jadi 1 proses kalau tetap mau pakai Kafka. Worth dipilih kalau kelompok mau eksplisit unjuk pemahaman log-based replay/offset di laporan, atau memang mengarah ke throughput besar buat M2.
  • NATS JetStream — ringan (single binary), replay + at-least-once built-in, resource footprint kecil, bahkan lebih hemat dari RabbitMQ maupun Kafka. Kandidat kuat, cuma kurang lazim didokumentasikan/diajarkan dibanding Kafka/RabbitMQ — worth dicoba kalau mau paling irit resource laptop saat demo.
  • Redis Pub/Sub — sengaja tidak dipilih: sifatnya fire-and-forget, tidak ada persistensi, jadi event yang terbit saat consumer mati pasti hilang, tidak bisa didemokan skenario “tersusul” — gagal memenuhi kriteria 3 kecuali kelompok memang mau eksplisit menunjukkan semantik at-most-once/loss sebagai pilihan sadar.

Glossarium Istilah Teknis

Istilah yang muncul di soal & rancangan solusi di atas, diurutkan alfabetis. Penjelasan dikaitkan ke konteks tugas ini, bukan definisi generik buku teks.

Access token — “tiket masuk” berumur pendek (default TTL 60 detik di tugas ini) yang dipegang client (Media/Tim Lapangan/Internal Ops) untuk membuktikan identitas + hak aksesnya tiap kali memanggil Client-Facing API. Beda dari refresh token (umurnya lebih panjang, dipakai khusus untuk menukar access token baru).

Additive-only (schema policy) — kebijakan evolusi skema yang cuma mengizinkan penambahan field baru, tidak pernah menghapus/mengubah tipe field lama. Dipilih di Problem 1 supaya kode lama tetap jalan begitu field baru (confidence_level) muncul.

API Gateway — komponen yang jadi satu pintu masuk untuk banyak client, biasanya menangani routing, auth, rate limiting di satu tempat. Di soal ini perannya dipegang Client-Facing API; kalau pakai tool jadi (Kong/Envoy) namanya juga API Gateway, tapi di tugas ini dibangun custom.

At-least-once / at-most-once delivery — jaminan pengiriman pesan dari message broker. At-least-once: pesan dijamin sampai, tapi bisa terkirim dobel (makanya consumer wajib idempoten). At-most-once: pesan terkirim maksimal sekali, tapi bisa hilang kalau consumer lagi mati. Relevan di Problem 5 saat menjelaskan nasib event yang terbit selagi consumer mati.

Avro / Protobuf — format serialisasi data biner dengan skema eksplisit (beda dari JSON yang bebas bentuk). Dipakai bareng schema registry untuk evolusi skema yang lebih formal — disinggung sebagai alternatif Problem 1, tapi tidak dipilih karena kontrak mock pakai REST/JSON, bukan biner.

Bulkhead (pattern) — meniru sekat kedap air di kapal: alokasikan resource (connection pool, thread pool) terpisah per dependency, supaya satu dependency yang lambat (PVMBG) tidak menghabiskan resource yang dibutuhkan dependency lain (BMKG). Bagian dari solusi Problem 2.

Canonical Store / data kanonik — penyimpanan tunggal milik Aggregator yang menyimpan HazardEvent (bentuk data yang sudah diseragamkan dari BMKG & PVMBG). “Kanonik” = versi resmi/acuan yang dipakai semua pihak, bukan bentuk asli masing-masing sumber.

Circuit breaker — pola yang “mematikan sirkuit” pemanggilan ke dependency yang lagi bermasalah (analog sekring listrik): setelah gagal beruntun, breaker terbuka dan berhenti mencoba memanggil PVMBG untuk sementara, langsung sajikan fallback (data lama + stale_since) alih-alih menunggu timeout tiap request. Dipakai di Problem 2.

Consumer group / offset — istilah Kafka. Consumer group = sekumpulan consumer yang berbagi beban baca satu topic. Offset = penanda posisi terakhir yang sudah dibaca tiap consumer group di topic itu — inilah yang bikin consumer yang mati bisa “tersusul” pesan yang terlewat begitu nyala lagi (baca dari offset terakhirnya).

Container / Dockerfile / orchestration — container = unit yang membungkus satu service + dependency-nya supaya bisa jalan konsisten di mana saja; Dockerfile = resep untuk membangun satu container; orchestration (mis. docker compose up) = perintah yang menyalakan banyak container sekaligus sesuai definisi hubungan antar-servicenya.

Correlation ID — ID unik yang dilekatkan ke satu request sejak masuk sistem, lalu diteruskan ke setiap service yang ikut memprosesnya, supaya log dari service berbeda bisa “disambungkan” jadi satu cerita ketika debugging satu alur permintaan.

Exchange / Queue / Binding (RabbitMQ) — istilah RabbitMQ. Exchange = titik masuk tempat producer publish pesan (di tugas ini pakai tipe fanout/topic supaya pesan disebar ke semua langganan, bukan cuma satu). Queue = tempat pesan “antre” menunggu dibaca satu consumer tertentu — tiap consumer wajib punya queue durable sendiri. Binding = koneksi yang menghubungkan exchange ke queue, menentukan queue mana saja yang kebagian pesan dari exchange itu. Kalau semua consumer dipaksa berbagi satu queue yang sama, itu jadi competing consumers (gantian), bukan fan-out.

Fan-out — satu event dikirim/disebarkan ke banyak penerima sekaligus (dashboard, notifier, dst.) tanpa si pengirim harus tahu siapa saja penerimanya satu per satu — inti dari Problem 5.

Graceful degradation — sistem tetap memberi sebagian layanan yang masih bisa dipenuhi saat sebagian lain gagal (mis. tetap jawab data seismik walau PVMBG mati), lawan dari “semua ikut gagal kalau satu bagian mati”. Kebalikan dari ini disebut kegagalan yang di soal disebut ketiadaan degradasi terkendali.

Head-of-Line (HOL) blocking — antrean tertahan gara-gara item paling depan lambat diproses, padahal item di belakangnya sebenarnya bisa selesai duluan. Di Problem 2: request yang cuma butuh BMKG ikut menunggu karena alurnya manggil PVMBG dulu secara sekuensial.

Idempoten — sifat operasi yang hasilnya sama walau dijalankan berkali-kali dengan input yang sama (mis. “simpan event dengan id X” — dipanggil 2x hasil akhirnya tetap satu event X, bukan dobel). Wajib dimiliki consumer Problem 5 karena broker at-least-once bisa mengirim pesan dobel.

JSONB — tipe kolom di PostgreSQL yang menyimpan JSON dalam bentuk yang bisa di-index & di-query, dipakai sebagai alternatif Problem 4: kolom biasa untuk field yang stabil, JSONB khusus untuk attributes yang bentuknya berubah-ubah.

JWT (JSON Web Token) — format token yang isinya (mis. scope, waktu kedaluwarsa) “ditandatangani” secara digital sehingga bisa diverifikasi tanpa perlu nanya balik ke database (stateless). Dipakai sebagai access token di Problem 3.

Message broker — perantara yang menampung & menyalurkan pesan dari pengirim (producer) ke penerima (consumer), sehingga keduanya tidak perlu tahu/terhubung langsung satu sama lain. Contoh: Kafka, RabbitMQ, NATS. Inti Problem 5.

Microservices — gaya arsitektur di mana sistem dipecah jadi beberapa service kecil yang masing-masing punya tanggung jawab sempit, storage sendiri, dan bisa di-deploy terpisah — lawan dari monolith (semua logic jadi satu unit deploy, seperti gateway Astolfo versi awal di Problem 4).

mTLS (mutual TLS) — versi HTTPS di mana kedua pihak (bukan cuma server) membuktikan identitas pakai sertifikat digital. Disinggung sebagai alternatif Problem 3 yang lebih aman tapi lebih berat operasionalnya (perlu kelola sertifikat tiap service).

p50 / p95 / p99 (percentile latency) — cara meringkas sebaran waktu respons: p95 artinya 95% request selesai lebih cepat dari angka itu (5% terlambat/terluar). Dipakai di kriteria Problem 2 karena lebih jujur dari rata-rata (rata-rata gampang “tertutupi” oleh mayoritas request cepat walau ada yang sangat lambat).

Polling vs push — polling: pihak penerima yang aktif nanya “ada data baru?” secara berkala (ini yang dipakai BNPB→mock di tugas ini). push: pihak pengirim yang aktif mengirim begitu ada data baru, tanpa ditanya (mock di tugas ini sengaja tidak push).

Rate limiter — mekanisme yang membatasi jumlah request yang diizinkan lewat dalam periode waktu tertentu, kelebihannya ditolak (mis. HTTP 429) alih-alih diterima semua sampai sistem kewalahan. Bagian solusi Problem 2 untuk mencegah resource exhaustion.

Resource exhaustion — resource sistem (koneksi, thread, memori) habis karena tidak ada batas jumlah request yang ditangani bersamaan, membuat sistem berhenti melayani semua pihak, bukan cuma yang berlebih. Salah satu dari 3 kegagalan Problem 2.

Schema registry — service terpisah yang menyimpan & memvalidasi skema data (biasa dipakai bareng Avro/Protobuf) supaya perubahan skema tercatat dan kompatibilitasnya bisa dicek otomatis. Disebut sebagai alternatif “lebih formal” untuk Problem 1.

Scope (token) — label di dalam token yang menyatakan “boleh akses apa saja” — di tugas ini nilainya media / lapangan / internal, dipakai Client-Facing API untuk memutuskan field mana yang boleh dikembalikan ke client tersebut.

Stale data / stale_since — data yang sudah tidak lagi paling baru (mis. data vulkanik terakhir sebelum PVMBG outage), ditandai kapan mulai basi (stale_since) supaya client tahu data itu bukan data real-time, bukan disembunyikan sebagai data segar.

Throughput — jumlah request yang berhasil diproses sistem per satuan waktu (mis. request/detik). Dilaporkan bareng p50/p95/p99 & error rate di hasil load testing Problem 2.

Tolerant reader — cara membaca/parsing data yang “longgar”: ambil field yang dikenal, jangan crash kalau ada field tambahan yang tidak dikenal (cukup diteruskan atau diabaikan sesuai kebijakan). Pendekatan utama yang dipilih untuk Problem 1.

Topic (message broker) — istilah Kafka/NATS untuk “saluran” bernama tempat producer publish pesan dan consumer subscribe, mirip nama grup siaran. Padanan di RabbitMQ = exchange (lihat entri Exchange/Queue/Binding). Di Problem 5, semua HazardEvent dipublikasikan ke satu saluran yang consumer manapun bisa berlangganan.

Next step

  • Kelompok sudah terbentuk? cek sheets sebelum 2026-09-19
  • Diskusikan pilihan storage P4 (Mongo vs Postgres+JSONB) dan broker P5 (Kafka vs RabbitMQ vs NATS) dengan anggota kelompok, putuskan final
  • Mulai dari mock BMKG & PVMBG dulu (kontraknya sudah fix, tidak bergantung keputusan arsitektur BNPB)

Files Terkait