Lompat ke konten
dokumentasi.id
Commit 65055c7
v1
Star821

2

Arsitektur Sistem

Repository ini adalah satu aplikasi Express kecil: satu entry point api/server.js, satu Router di api/routes.js, dua handler, satu modul komputasi di api/lib/, dan dua berkas JSON sebagai sumber data. Di sisi lain ada crawler/ yang berdiri sendiri sebagai perkakas offline, tidak pernah di-require oleh kode api/. Seluruh ketergantungan mengalir satu arah: route memanggil handler, handler memakai lib atau membaca JSON, dan tidak ada lapisan yang memanggil balik lapisan di atasnya.

Peta modul dan arah ketergantungan

Diagram berikut menunjukkan arah require yang sebenarnya. api/routes.js mengimpor ./middlewares dan kedua handler api/routes.js:1-5. api/handlers/juz.js menyerahkan pekerjaan berat ke ../lib/juz.js api/handlers/juz.js:1-6. Modul lib dan handler surah membaca berkas JSON secara langsung api/handlers/surah.js:1, api/lib/juz.js:1-2.

graph TD
  server["api/server.js"] --> routes["api/routes.js"]
  routes --> caching["api/middlewares.js — caching"]
  routes --> surahHandler["handlers/surah.js"]
  routes --> juzHandler["handlers/juz.js"]
  juzHandler --> juzLib["api/lib/juz.js"]
  surahHandler --> quranJson[("data/quran.json")]
  juzLib --> quranJson
  juzLib --> juzJson[("data/juz.json")]
  style server fill:#f59e0b,color:#000
  style routes fill:#f59e0b,color:#000
  style caching fill:#8b5cf6,color:#fff
  style surahHandler fill:#2d7d46,color:#fff
  style juzHandler fill:#2d7d46,color:#fff
  style juzLib fill:#8b5cf6,color:#fff
  style quranJson fill:#336791,color:#fff
  style juzJson fill:#336791,color:#fff
%% fig: Arah ketergantungan modul runtime
%% desc: Route menjadi satu-satunya pemanggil handler, handler memakai lib atau membaca JSON, dan data JSON tidak pernah mengimpor kode api.
Fig. 1: Arah ketergantungan modul runtimeRoute menjadi satu-satunya pemanggil handler, handler memakai lib atau membaca JSON, dan data JSON tidak pernah mengimpor kode api.

Tidak ada panah balik dari handler ke route, dan tidak ada modul api/ yang menyentuh direktori crawler/. Konsekuensinya, setiap perubahan kontrak response hanya perlu dilakukan di handler, tanpa menyentuh routing.

Tanggung jawab tiap lapisan

ModulTanggung jawabTitik masuk
api/server.jsMembuat instance Express, memasang cors() dan express.json(), lalu memanggil listen pada process.env.PORTapi/server.js:8-18
api/routes.jsSatu Router yang memetakan URL ke handler, menulis header Cache-Control untuk semua response, dan menutup dengan fallback 404api/routes.js:9-12, api/routes.js:34-38, api/routes.js:41-45
api/middlewares.jsMiddleware caching yang menyimpan res.send asli dan menyalin body ke objek cacheapi/middlewares.js:1-17
api/handlers/surah.jsTiga method static: getAllSurah, getSurah, getAyahFromSurah, masing-masing menyusun body { code, status, message, data }api/handlers/surah.js:5-10
api/handlers/juz.jsSatu method static getJuz yang mem-parse req.params.juz lalu memanggil juzDataapi/handlers/juz.js:1-6
api/lib/juz.jsFungsi juzData yang memotong rentang verses dari quran.json memakai batas pada juz.jsonapi/lib/juz.js:1-2
crawler/Skrip npm run crawl: mengambil data sumber, menyusun ulang objek surah, lalu menulis ./data/quran.jsoncrawler/script.js:196-203
config.jsMembaca .env dan mengekspor SOURCE_API_BASEURL, SOURCE_API_BASEURL_2, MONGODB_URIconfig.js:1-11
vercel.jsonMenetapkan api/server.js sebagai satu-satunya build dan tujuan seluruh routevercel.json:3-10

api/server.js hanya merangkai

Berkas entry point ini hanya berisi 18 baris: memuat dotenv, membuat app, mengaktifkan trust proxy, memasang cors(), memasang express.json(), memasang router, lalu listen ke port api/server.js:8-18. Tidak ada rate limiter, logging middleware, maupun penanganan error terpusat di lapisan ini. Skrip start dan dev di package.json sama-sama menunjuk ke berkas tersebut package.json:4-14.

api/routes.js sebagai satu-satunya pintu masuk

Router ini melakukan tiga hal. Pertama, middleware tanpa path menulis Cache-Control: public, max-age=0, s-maxage=86400, stale-while-revalidate ke setiap response, termasuk yang berakhir 404 api/routes.js:9-12. Kedua, empat route data didaftarkan dengan caching di antara path dan handler api/routes.js:34-38. Ketiga, router.all('*') menutup semua path lain dengan body code: 404 dan pesan Resource "${req.url}" is not found. api/routes.js:41-45.

/juz/:juz tidak memakai caching, sedangkan /surah, /surah/:surah, dan /surah/:surah/:ayah memakainya. Perilaku middleware itu dibahas di Alur Request dan Middleware caching, dan daftar lengkap route ada di Referensi Endpoint.

Handler menjawab, lib menghitung

Pembagiannya konsisten: handler surah bekerja langsung pada array quran yang di-require di baris pertama berkas, misalnya getAllSurah menyalin tiap item lalu menghapus verses dan preBismillah sebelum dikirim api/handlers/surah.js:5-10. Handler juz justru tidak menyentuh JSON sama sekali; ia hanya memvalidasi hasil juzData(parseInt(juz)) lalu mengubah null menjadi 404 api/handlers/juz.js:1-6. Semua aritmetika rentang ayat berada di juzData pada api/lib/juz.js.

Bentuk field yang dikirim handler bergantung pada struktur data/quran.json dan data/juz.json; keduanya dijelaskan di Model Data quran.json dan juz.json, sedangkan algoritme pemotongan rentangnya di Komputasi Rentang Juz.

Data JSON dibaca sekali saat modul dimuat

Kedua sumber data diimpor dengan require di tingkat atas modul api/handlers/surah.js:1, api/lib/juz.js:1-2. Artinya Node mem-parse JSON itu sekali per proses, lalu menyimpannya di module cache. Dua konsekuensi praktis: mengganti isi data/quran.json tidak berpengaruh pada proses yang sedang berjalan, dan setiap instance serverless memegang salinan penuh datanya sendiri.

graph LR
  env[".env"] --> config["config.js"]
  config --> script["crawler/script.js"]
  helpers["crawler/helpers.js"] --> script
  script -->|"writeFile"| quranFile[("data/quran.json")]
  quranFile -->|"require saat boot"| handler["handlers/surah.js"]
  quranFile --> lib["api/lib/juz.js"]
  juzFile[("data/juz.json")] --> lib
  style env fill:#f59e0b,color:#000
  style config fill:#f59e0b,color:#000
  style script fill:#2d7d46,color:#fff
  style helpers fill:#2d7d46,color:#fff
  style quranFile fill:#336791,color:#fff
  style juzFile fill:#336791,color:#fff
  style handler fill:#8b5cf6,color:#fff
  style lib fill:#8b5cf6,color:#fff
%% fig: Siklus data dari crawler ke runtime
%% desc: Crawler menulis data/quran.json, sementara runtime hanya membacanya saat modul di-require; panah menuju handler berarti ketergantungan baca, bukan pemanggilan kode.
Fig. 2: Siklus data dari crawler ke runtimeCrawler menulis data/quran.json, sementara runtime hanya membacanya saat modul di-require; panah menuju handler berarti ketergantungan baca, bukan pemanggilan kode.

data/quran.json ditulis oleh writeFile di akhir main() crawler/script.js:196-203, dan tidak ada jalur kode di api/ yang menulis berkas itu kembali. Rantai crawler lengkapnya ada di Crawler: Pipeline Pengambilan Data.

⚠️ crawler/script.js me-require('../data/quran-tafsir.json') di tingkat atas crawler/script.js:2-10, tetapi berkas itu tidak ada di repository pada commit ini. npm run crawl akan gagal saat modul dimuat, sebelum satu pun fetch dijalankan.

Satu entry point untuk semua target deploy

Baik Vercel maupun Heroku mengarah ke berkas yang sama. vercel.json menetapkan @vercel/node untuk api/server.js dan mengarahkan /.* ke sana vercel.json:3-10, sementara package.json menjadikannya main serta target skrip start package.json:4-14. Lingkungan dan variabel yang dibutuhkan crawler dibaca lewat config.js config.js:1-11; detail menjalankan dan men-deploy ada di Konfigurasi, Menjalankan Lokal, dan Deployment.