Sampul buku
Blueprint Arsitektur & Kecepatan Bikin Aplikasi · Edisi Pertama

SaaS atau On-Premise? Hybrid Aja Deh

Modular Monolith untuk Bikin Aplikasi Cepat — rumus yang bisa diikuti manusia maupun agent AI: satu aplikasi dalam semalam, sekitar tiga puluh aplikasi dalam sebulan.
Ditulis oleh Galih Prasetyo
Edisi Pertama · 2026 · Blueprint untuk Manusia & Agent AI
Pembuka

Kata Pengantar

Ada satu pertanyaan yang selalu muncul di ruang rapat, di kolom komentar, dan di kepala setiap orang yang hendak membangun perangkat lunak: SaaS atau on-premise? Pertanyaan itu terdengar seperti pilihan hidup-atau-mati, seolah salah pilih berarti proyek gagal. Buku ini hadir untuk menurunkan tensi perdebatan itu dan menggantinya dengan sesuatu yang lebih berguna: sebuah rumus.

Judulnya sengaja jenaka — "Hybrid Aja Deh" — karena begitulah jawaban yang paling sering benar dalam praktik. Bukan karena hybrid selalu superior secara teknis, melainkan karena, ketika tujuan Anda adalah bikin aplikasi dengan cepat, model penyebaran seharusnya menjadi keputusan terakhir, bukan pertama. Yang menentukan kecepatan bukanlah pilihan cloud versus server klien, melainkan bagaimana kode Anda ditata. Dan di situlah Modular Monolith masuk sebagai bintang utama buku ini.

Buku ini punya dua pembaca yang setara pentingnya. Yang pertama adalah Anda, manusia — developer solo, tim kecil, founder teknis, atau vibe-coder yang ingin mengubah ide jadi produk sebelum semangatnya padam. Yang kedua adalah agent AI — asisten pemrograman yang Anda perintah untuk menuliskan kode. Keduanya butuh hal yang sama: langkah eksplisit, batas yang jelas, dan resep yang bisa dieksekusi bertahap tanpa menebak-nebak. Itulah kenapa setiap bab ditulis dalam HTML semantik dengan heading terstruktur, daftar bernomor, dan blok kode yang bisa disalin.

Organisasi yang merancang sistem akan menghasilkan desain yang menyalin struktur komunikasi organisasi itu sendiri.

Melvin E. Conway, 1967 — "Conway's Law"

Conway mengingatkan bahwa arsitektur perangkat lunak mencerminkan cara tim berkomunikasi. Jika Anda tim satu orang — atau satu orang plus satu agent AI — maka arsitektur yang paling jujur bagi Anda bukanlah dua belas microservices yang saling memanggil lewat jaringan, melainkan satu monolit yang tertata rapi ke dalam modul-modul. Buku ini akan membela posisi itu habis-habisan, sambil tetap jujur tentang kapan Anda perlu keluar darinya.

Panduan Pemakaian

Cara Membaca Buku Ini

Buku ini bisa dibaca lurus dari depan ke belakang, tetapi ia dirancang agar bisa juga dilompati. Setiap bab berdiri cukup mandiri sehingga Anda — atau agent Anda — dapat masuk langsung ke resep yang dibutuhkan.

Peta empat bagian

Konvensi penanda

◆ Kotak Tip

Berisi jalan pintas atau praktik terbaik yang mempercepat pekerjaan.

▲ Kotak Awas

Menandai jebakan umum — biasanya sumber over-engineering atau utang teknis.

■ Kotak Rumus

Aturan padat yang bisa dihafal atau ditempel di dinding.

✦ Untuk Agent AI

Instruksi yang ditujukan langsung kepada asisten AI: prasyarat, urutan, dan kriteria selesai untuk satu langkah.

■ Rumus Induk Buku Ini

Use case sempit × stack tetap × modul siap pakai × Modular Monolith = 1 aplikasi per malam. Ulangi resep yang sama dengan template yang di-reuse = ~30 aplikasi per bulan. Model penyebaran (SaaS / On-Prem / Hybrid) ditentukan terakhir, sesuai kebutuhan pelanggan, bukan selera.

Tujuh prinsip yang menyatukan buku ini

Seluruh bab tunduk pada tujuh prinsip berikut. Bila suatu saran terasa bertentangan dengan salah satunya, prinsip yang menang.

  1. Scope sempit menang. Aplikasi yang melakukan satu hal dengan baik selesai dalam semalam; yang melakukan sepuluh hal tak pernah selesai.
  2. Stack tetap. Keakraban mengalahkan kebaruan. Satu stack, dikuasai, dipakai berulang.
  3. Modul berbatas. Setiap domain di modules/<domain> dengan kontrak publik tunggal.
  4. Reuse di atas rewrite. Template & modul siap pakai adalah aset; menulis ulang adalah utang.
  5. Tunda penyebaran. SaaS/On-Prem/Hybrid adalah saklar config, bukan fondasi.
  6. Aman secara default. Kesalahan keamanan dibuat mustahil di lapisan akses, bukan sekadar dilarang.
  7. Tolak over-engineering. Tak membangun untuk kebutuhan yang belum ada.
✦ Untuk Agent AI — cara memakai buku ini

Perlakukan buku ini sebagai basis pengetahuan yang dapat dieksekusi. Tiap bab punya id anchor (mis. #bab-6); tiap resep adalah daftar bernomor dengan kriteria selesai; tiap blok kode bertanda bahasa. Mulai dari Generator (Bab 6) untuk spesifikasi, ikuti kontrak eksekusi (Bab 13), dan patuhi tujuh prinsip di atas sepanjang pengerjaan.

Bagian I

Fondasi Arsitektur

Empat bab pertama membangun kosakata bersama. Kita bedah tiga model penyebaran — SaaS, on-premise, hybrid — apa adanya, lalu memperkenalkan pola kode yang membuat ketiganya bisa dijalankan dari satu basis kode yang sama: Modular Monolith. Setelah bagian ini, Anda tidak lagi memilih model berdasarkan tren, melainkan berdasarkan kebutuhan.

Bab Satu

SaaS vs On-Premise vs Hybrid

Sebelum menulis satu baris kode pun, kita perlu sepakat soal istilah. Tiga kata ini — SaaS, on-premise, hybrid — sering dilempar seolah maknanya jelas, padahal banyak proyek tersesat justru karena definisinya kabur. Mari kita luruskan dengan bahasa yang paling awam.

Tiga model dalam bahasa sehari-hari

SaaS (Software as a Service) adalah perangkat lunak yang Anda sewa, bukan Anda miliki. Aplikasinya berjalan di server milik pembuatnya (di cloud), dan pelanggan cukup membuka peramban lalu masuk. Analoginya seperti naik ojek online: Anda tidak punya motornya, tidak mengurus servis dan bensin, tinggal pakai dan bayar sesuai pemakaian. Gmail, Notion, dan Spotify adalah SaaS.

On-Premise (sering disingkat on-prem) adalah kebalikannya: perangkat lunak dipasang dan berjalan di server milik pelanggan sendiri, di dalam kantor atau data center mereka. Analoginya seperti membeli mobil pribadi: mahal di depan, Anda yang mengurus servis, tetapi mobil dan semua isinya sepenuhnya milik dan kendali Anda. Bank, rumah sakit, dan instansi pemerintah sering mensyaratkan on-prem karena datanya tak boleh keluar gedung.

Hybrid adalah campuran keduanya: sebagian berjalan di cloud, sebagian di server klien. Analoginya seperti mobil pribadi dengan langganan layanan navigasi: mobilnya milik Anda (data & aplikasi inti di on-prem), tetapi peta dan lalu lintas real-time datang dari server pusat (lisensi, sinkronisasi, atau AI di cloud). Hybrid muncul ketika ada kebutuhan yang saling bertentangan — data harus lokal, tetapi lisensi dan pembaruan ingin dikendalikan terpusat.

SaaS Cloud Vendor Banyak pelanggan On-Premise Server Klien Data tak keluar Gedung pelanggan Hybrid Lisensi/Sync (cloud) Inti (on-prem) Dua dunia disambung
Gambar 1.1 — Tiga model penyebaran: siapa yang memegang server, dan di mana data tinggal.

Empat sumbu keputusan

Alih-alih bertanya "mana yang terbaik", tanyakan empat hal ini. Jawabannya menuntun Anda ke model yang tepat tanpa drama.

SumbuCondong SaaS bila…Condong On-Prem bila…
Kepemilikan dataData boleh di cloud; pelanggan tak keberatan.Regulasi/kontrak mewajibkan data tinggal di gedung klien.
Model pendapatanLangganan bulanan, banyak pelanggan kecil.Lisensi besar per instalasi, sedikit pelanggan enterprise.
Kendali pembaruanAnda ingin update terpusat, semua pelanggan versi sama.Klien mengendalikan kapan update dipasang.
Kecepatan rilisIngin rilis harian; iterasi cepat.Rilis terjadwal, uji ketat sebelum masuk produksi klien.

Lima mitos yang memperlambat

Sebelum lanjut, mari bongkar keyakinan keliru yang paling sering membuat proyek melambat sebelum baris kode pertama ditulis.

MitosKenyataan
"Kalau serius, harus microservices."Untuk tim kecil, microservices menambah beban tanpa manfaat. Monolit modular lebih cepat & cukup untuk mayoritas produk.
"Model penyebaran ditentukan di awal."Ia bisa — dan sebaiknya — ditunda. Dengan Modular Monolith, penyebaran hanyalah konfigurasi build.
"On-prem itu kuno."On-prem tetap wajib di sektor teregulasi. Yang berubah hanya cara mengemasnya: kini via kontainer.
"SaaS selalu lebih murah."Biaya cloud tumbuh seiring pemakaian. Untuk beban stabil, VPS atau on-prem bisa lebih ekonomis.
"Cepat berarti berantakan."Cepat yang berkelanjutan justru datang dari kerapian: konvensi tetap, modul berbatas, template reuse.

Kenapa "Hybrid + Modular Monolith" sering jadi jawaban

Bila Anda mengejar kecepatan, kabar baiknya: Anda tidak harus memilih di awal. Kunci nya adalah menunda keputusan penyebaran hingga saat Anda benar-benar tahu siapa pelanggannya. Ini hanya mungkin jika kode Anda tidak mengunci diri ke satu model.

Modular Monolith memungkinkan hal itu. Satu basis kode, tertata ke dalam modul-modul dengan batas jelas, bisa:

Karena modul-modul itu berbicara lewat pemanggilan fungsi di dalam satu proses (bukan jaringan), Anda menghindari seluruh kompleksitas microservices yang justru memperlambat tim kecil. Model penyebaran menjadi sekadar konfigurasi build, bukan keputusan arsitektur yang mahal.

Kesederhanaan adalah prasyarat keandalan.

Edsger W. Dijkstra
■ Rumus Bab 1

Jangan mulai dari "SaaS atau on-prem". Mulai dari use case dan Modular Monolith. Biarkan model penyebaran menjadi keputusan yang bisa diubah, bukan fondasi yang tak bisa dicabut.

Pohon keputusan model penyebaran

Bila Anda perlu memutuskan model sekarang, ikuti pohon ini. Ia sengaja pendek — sebagian besar proyek berhenti di dua pertanyaan pertama.

Data wajib tinggaldi gedung klien? tidak Banyak pelanggankecil, langganan? ya SaaS ya Perlu lisensi/billingterpusat? tidak On-Premise Hybrid Apa pun cabangnya, basis kodenya sama: satu Modular Monolith.
Gambar 1.2 — Dua-tiga pertanyaan cukup untuk memilih model. Kodenya tetap satu.

Tujuan besar buku ini, ditegaskan

Sepanjang halaman berikutnya, satu obsesi akan terus muncul: kecepatan yang berkelanjutan. Bukan cepat lalu ambruk, melainkan cepat karena punya sistem. Dua angka menjadi jangkar:

1 aplikasi / semalam ~30 aplikasi / sebulan 0 microservice yang tak perlu

Angka-angka itu bukan sihir. Ia hasil dari disiplin: stack yang tidak pernah diganti, modul yang di-reuse, konvensi yang tidak diperdebatkan ulang, dan penolakan keras terhadap over-engineering. Buku ini adalah manual disiplin tersebut — cukup eksplisit untuk diikuti manusia, cukup terstruktur untuk dieksekusi agent AI.

Bab Dua

SaaS Mendalam

SaaS terlihat sederhana dari luar — "aplikasi web yang bisa dilanggan" — tetapi di dalamnya tersimpan satu keputusan yang membentuk seluruh arsitektur: bagaimana Anda melayani banyak pelanggan dari satu aplikasi tanpa data mereka saling bocor. Inilah persoalan multi-tenancy.

Ada dua cara membangun desain perangkat lunak: membuatnya begitu sederhana sehingga jelas tak ada kekurangan, atau begitu rumit sehingga tak ada kekurangan yang jelas.

C. A. R. Hoare

Apa itu tenant

Dalam SaaS, tenant adalah satu unit pelanggan yang datanya terisolasi dari pelanggan lain. Tenant bisa berarti satu perusahaan, satu tim, atau satu akun. Aplikasi yang sama melayani ribuan tenant, tetapi tenant A tidak boleh pernah melihat data tenant B. Cara Anda memenuhi janji isolasi ini menentukan tiga pola besar.

Tiga pola multi-tenancy: pool, silo, bridge

1. Pool (shared everything)

Semua tenant berbagi satu basis data dan tabel yang sama. Pemisahan dilakukan dengan kolom tenant_id di setiap baris, dan setiap query wajib menyaring berdasarkan kolom itu. Ini pola termurah dan tercepat untuk memulai — ideal untuk SaaS kecil dengan banyak tenant ringan.

prisma
// Pola POOL: satu tabel, dipisah kolom tenantId
model Invoice {
  id        String   @id @default(cuid())
  tenantId  String   // wajib ada di SETIAP tabel tenant
  amount    Int
  createdAt DateTime @default(now())
  @@index([tenantId])            // selalu indeks tenantId
}
▲ Awas: kebocoran tenant

Dengan pola pool, satu query yang lupa menambahkan where tenantId bisa membocorkan seluruh data pelanggan. Jangan andalkan disiplin manual — bungkus akses data di satu lapisan (repository/service) yang selalu menyuntik tenantId. Lihat Bab 11.

2. Silo (isolated everything)

Setiap tenant punya basis data (atau skema) sendiri yang benar-benar terpisah. Isolasi kuat, cocok untuk pelanggan enterprise yang menuntut jaminan. Harganya: biaya operasional dan kompleksitas migrasi naik seiring jumlah tenant.

3. Bridge (shared app, separate schema)

Jalan tengah: satu aplikasi dan satu server basis data, tetapi masing-masing tenant mendapat skema tersendiri. Menyeimbangkan isolasi dan biaya. Cocok saat sebagian tenant butuh jaminan lebih tanpa harus membangun infrastruktur silo penuh.

AspekPoolSiloBridge
Isolasi dataLogis (kolom)Fisik (DB terpisah)Skema terpisah
Biaya per tenantSangat rendahTinggiSedang
Kompleksitas migrasiSatu kali× jumlah tenant× jumlah skema
Cocok untukSaaS massal, tenant ringanEnterprise, regulasi ketatCampuran
Risiko kebocoranPerlu guard ketatMinimalRendah
◆ Tip: mulai dari pool

Untuk 1 app semalam, hampir selalu pilih pool. Ia paling cepat dibangun dan paling murah dijalankan. Naikkan tenant tertentu ke bridge/silo hanya ketika ada pelanggan yang membayar untuk isolasi itu — bukan sebelumnya.

Langganan & billing

SaaS hidup dari pendapatan berulang. Model langganan yang umum:

Secara teknis, billing SaaS bersandar pada webhook dari penyedia pembayaran (Stripe, Midtrans, Xendit). Alurnya: pelanggan membayar → penyedia mengirim webhook → aplikasi memperbarui status langganan tenant → fitur terbuka/terkunci lewat feature flag (Bab 8).

typescript
// modules/billing/webhook.ts — inti pola webhook langganan
export async function handleWebhook(evt: BillingEvent) {
  switch (evt.type) {
    case 'subscription.active':
      await setPlan(evt.tenantId, evt.plan, 'active');
      break;
    case 'subscription.past_due':
      await setPlan(evt.tenantId, evt.plan, 'grace'); // beri tenggang
      break;
    case 'subscription.canceled':
      await setPlan(evt.tenantId, 'free', 'canceled');
      break;
  }
}

Merancang tingkatan (tier) yang masuk akal

Kebanyakan SaaS berjenjang. Rancang tingkatan berdasarkan nilai yang dirasakan pelanggan, bukan sekadar membatasi fitur secara acak. Pola yang teruji:

TingkatUntuk siapaPembatas alami
FreeMencoba, proyek kecilKuota rendah (mis. 5 invoice/bulan)
ProIndividu/tim kecil seriusKuota longgar + fitur produktivitas
BusinessTim/perusahaanSeat banyak, SSO, dukungan prioritas
EnterpriseKlien besar/teregulasiKontrak, on-prem/hybrid, SLA

Perhatikan bahwa tingkat Enterprise sering kali adalah pintu menuju on-prem atau hybrid — inilah tempat ketiga model bertemu dalam satu produk. Karena basis kode Anda Modular Monolith, melayani pelanggan enterprise on-prem tidak berarti membangun produk kedua.

◆ Tip: kunci fitur, bukan cabang kode

Semua tingkat berjalan di kode yang sama. Perbedaannya hanya feature flag (Bab 8) yang membaca plan tenant. Jangan pernah membuat cabang kode terpisah per tingkat — itu menggandakan beban pemeliharaan tanpa alasan.

Onboarding tenant baru

Salah satu keputusan halus di SaaS pool adalah apa yang terjadi saat pelanggan baru mendaftar. Alurnya harus atomik: buat tenant, buat pengguna pemilik, tetapkan plan awal (biasanya free), lalu arahkan ke aplikasi. Semua dalam satu transaksi agar tak ada tenant setengah jadi.

typescript
// modules/tenant/onboard.ts — atomik, tak ada tenant setengah jadi
export async function onboard(email: string, org: string) {
  return db.$transaction(async (tx) => {
    const tenant = await tx.tenant.create({ data: { name: org, plan: 'free' } });
    await tx.user.create({ data: { email, tenantId: tenant.id, role: 'owner' } });
    return tenant;
  });
}
◆ Tip: seed data contoh

Saat onboarding, isi tenant baru dengan satu-dua data contoh (mis. satu klien, satu invoice draft). Pengguna yang melihat aplikasi "sudah ada isinya" jauh lebih cepat paham — dan ini menaikkan konversi dari coba menjadi bayar.

Update terpusat: berkah utama SaaS

Keunggulan terbesar SaaS bukan teknologi, melainkan operasional: semua pelanggan menjalankan satu versi. Perbaiki bug sekali, semua orang dapat perbaikannya. Rilis fitur sekali, semua orang melihatnya. Tidak ada "pelanggan X masih di versi lama". Inilah yang membuat iterasi harian mungkin — dan iterasi harian adalah bahan bakar rumus "30 app sebulan".

Kelebihan & kekurangan SaaS

KelebihanKekurangan
Rilis & perbaikan terpusat, semua pelanggan seragam.Data pelanggan berada di infrastruktur Anda — beban kepercayaan & kepatuhan.
Pendapatan berulang & dapat diprediksi.Perlu menanggung biaya cloud yang tumbuh seiring pemakaian.
Onboarding cepat: pelanggan tinggal daftar.Tidak cocok untuk klien yang melarang data keluar gedung.
Skala ekonomis: satu tim melayani ribuan tenant.Downtime Anda = downtime semua pelanggan sekaligus.
■ Kapan pilih SaaS

Pilih SaaS ketika: pelanggan Anda banyak dan kecil, data boleh di cloud, Anda ingin rilis cepat & terpusat, dan model bisnisnya langganan. Ini default untuk sebagian besar produk baru yang mengejar pertumbuhan.

Bab Tiga

On-Premise Mendalam

Bagi sebagian orang, on-premise terdengar kuno — peninggalan era sebelum cloud. Kenyataannya, on-prem tetap hidup dan bahkan wajib di banyak sektor. Alasannya satu kata: kedaulatan. Ada data yang, secara hukum atau prinsip, tidak boleh meninggalkan gedung pemiliknya.

Apa yang berubah ketika aplikasi dipasang di server klien

Pada SaaS, Anda mengendalikan server. Pada on-prem, Anda menyerahkan aplikasi untuk berjalan di lingkungan yang tidak Anda kendalikan. Ini mengubah beberapa asumsi mendasar:

◆ Tip: Docker Compose adalah teman on-prem

Kemas seluruh aplikasi — web, worker, database, cache — sebagai satu docker-compose.yml. Klien cukup menjalankan docker compose up -d. Ini menghilangkan 90% masalah "jalan di mesin saya tapi tidak di server klien". Detail di Bab 10.

Lisensi: bagaimana Anda dibayar tanpa mengendalikan server

Pada SaaS, kalau pelanggan berhenti bayar, Anda tinggal mematikan akun. Pada on-prem, aplikasinya ada di server klien — Anda tidak bisa sekadar "mematikan". Karena itu on-prem membutuhkan License Manager (dibahas penuh di Bab 9): sebuah mekanisme yang memvalidasi bahwa instalasi ini sah dan langganannya aktif.

Dua mode validasi lisensi:

Keamanan data di sisi klien

Ironisnya, on-prem dipilih demi keamanan, tetapi memindahkan tanggung jawab keamanan ke klien juga menambah risiko baru. Yang harus Anda sediakan:

Peramalan sangat sulit, terutama tentang masa depan.

Pepatah, sering dikaitkan dengan Niels Bohr

Kutipan itu relevan karena on-prem menuntut Anda merancang untuk lingkungan yang tak Anda ketahui. Semakin sedikit asumsi yang aplikasi Anda buat tentang lingkungannya, semakin mulus ia berjalan di server klien mana pun.

Sektor yang lazim menuntut on-prem

SektorPemicuModel lazim
Perbankan & keuanganRegulasi ketat, data transaksi sensitifOn-Prem / Hybrid
KesehatanKerahasiaan data pasienHybrid
Pemerintahan & pertahananKedaulatan data, sering air-gappedOn-Prem
ManufakturSistem di pabrik, jaringan terisolasiOn-Prem
Pendidikan besarData siswa, integrasi kampusHybrid
■ Kapan pilih On-Premise

Pilih on-prem ketika: regulasi/kontrak mewajibkan data tinggal di sisi klien (perbankan, kesehatan, pemerintah, pertahanan), klien punya tim IT sendiri, model bisnisnya lisensi enterprise besar, dan klien menuntut kendali penuh atas kapan update dipasang.

Checklist kesiapan on-prem

Sebelum mengirim aplikasi ke server klien pertama, pastikan hal-hal ini beres. Kelalaian di sini menjadi tiket dukungan berhari-hari.

Strategi versi & rilis on-prem

Pada SaaS Anda punya satu versi hidup. Pada on-prem, beberapa versi berjalan bersamaan di klien berbeda. Tanpa strategi, ini menjadi mimpi buruk dukungan. Tiga aturan menjaganya waras:

bash · pembaruan aman di klien
# klien menjalankan skrip pembaruan yang Anda sediakan
docker compose pull                       # tarik image versi baru
docker compose run --rm app ./migrate.sh  # migrasi maju-saja
docker compose up -d                      # nyalakan versi baru
docker compose run --rm app ./healthcheck.sh
◆ Tip: kirim catatan rilis yang manusiawi

Sertakan CHANGELOG singkat berbahasa manusia di tiap rilis on-prem — apa yang berubah, apa yang perlu diperhatikan, dan apakah ada tindakan manual. Tim IT klien akan berterima kasih, dan tiket dukungan Anda berkurang.

Kelebihan & kekurangan On-Premise

KelebihanKekurangan
Data tak pernah keluar gedung klien — kepatuhan mudah dibuktikan.Update lambat & harus mendukung banyak versi sekaligus.
Klien memegang kendali penuh atas infrastruktur.Debugging sulit tanpa akses langsung.
Pendapatan lisensi besar per instalasi.Onboarding lambat: instalasi, konfigurasi, pelatihan.
Bisa berjalan air-gapped (tanpa internet).Butuh License Manager & distribusi yang matang.
Bab Empat

Hybrid

Hybrid bukan model "malas memilih". Ia adalah jawaban yang tepat ketika dua kebutuhan yang saling bertentangan harus dipenuhi sekaligus: data harus lokal, tetapi kendali harus terpusat. Ketika Anda menemui keduanya dalam satu proyek, hybrid berhenti menjadi kompromi dan menjadi desain yang benar.

Anatomi sistem hybrid

Sistem hybrid membelah aplikasi menjadi dua wilayah yang berkomunikasi lewat kontrak yang jelas:

CLOUD — Bidang Kendali License Server Billing Sync & Update Registry ON-PREM — Bidang Inti Aplikasi (Modular Monolith) Database Klien (data sensitif) HTTPS Data tinggal di kanan · kendali di kiri
Gambar 4.1 — Hybrid membelah aplikasi jadi bidang inti (data) dan bidang kendali (lisensi/sinkronisasi).

Sinkronisasi: jantung yang berdenyut

Ketika sebagian data harus hadir di dua tempat, Anda memerlukan strategi sinkronisasi. Prinsip yang menyelamatkan waktu:

typescript
// modules/sync/push.ts — kirim metrik agregat, aman diulang
export async function pushUsage(period: string) {
  const payload = {
    idempotencyKey: `usage-${period}`,   // kunci idempoten: aman kirim ulang
    seats: await countActiveSeats(),      // agregat, BUKAN data mentah
    period,
  };
  try {
    await post(`${LICENSE_SERVER}/usage`, payload);
  } catch {
    await queue.enqueue(payload);         // offline? antre & coba lagi nanti
  }
}
▲ Awas: hybrid yang kebablasan

Godaan hybrid adalah menyinkronkan terlalu banyak — data mentah dua arah, real-time, dengan resolusi konflik rumit. Itu pintu masuk ke kompleksitas terdistribusi yang justru ingin kita hindari. Batasi sinkronisasi pada metrik agregat satu arah. Bila Anda merasa butuh lebih, tanyakan apakah kasusnya benar-benar hybrid atau sebenarnya dua produk terpisah.

License server pada hybrid

Pola hybrid paling umum di dunia B2B adalah aplikasi on-prem + license server di cloud. Aplikasi berjalan penuh di gedung klien, tetapi setiap beberapa jam ia memperpanjang token lisensi dari server Anda. Bila token gagal diperpanjang melewati masa tenggang, aplikasi masuk mode terbatas. Ini memberi Anda kendali komersial tanpa menyentuh data klien.

◆ Tip: masa tenggang itu wajib

Jangan pernah mematikan aplikasi klien seketika saat lisensi gagal divalidasi — jaringan bisa putus karena alasan sah. Beri masa tenggang (mis. 7–14 hari) sebelum membatasi fitur. Ini menjaga hubungan baik dan menghindari insiden akibat gangguan jaringan sesaat.

Ringkasan tiga model berdampingan

DimensiSaaSOn-PremiseHybrid
Lokasi dataCloud AndaServer klienKlien (inti) + cloud (kendali)
Kendali updateAnda, terpusatKlienTerbagi
Model bayarLanggananLisensiLisensi + langganan
Kecepatan rilisTertinggiTerjadwalSedang
Kompleksitas operasiRendahSedangTertinggi
Basis kodeSama — satu Modular Monolith untuk ketiganya
■ Kapan Hybrid ideal

Pilih hybrid ketika data harus tinggal di klien tetapi Anda tetap ingin: mengendalikan lisensi terpusat, menagih berdasarkan pemakaian, mendistribusikan pembaruan, atau menyediakan layanan AI berat yang tak praktis dipasang di setiap klien. Hybrid = kedaulatan data + kendali komersial.

Bab Lima

Modular Monolith

Inilah bab yang menjadi tulang punggung seluruh buku. Bila Anda hanya sempat membaca satu bab, baca yang ini. Modular Monolith adalah pola yang membuat "1 app semalam" mungkin, dan yang membuat aplikasi yang sama bisa di-deploy sebagai SaaS, on-prem, atau hybrid tanpa ditulis ulang.

Definisi yang jujur

Modular Monolith adalah satu aplikasi (satu proses, satu deployable) yang di dalamnya dibagi menjadi modul-modul dengan batas yang tegas. Setiap modul memiliki domain, data, dan antarmukanya sendiri, tetapi semuanya berjalan dalam proses yang sama dan berkomunikasi lewat pemanggilan fungsi, bukan panggilan jaringan.

Ia berdiri di antara dua ekstrem:

Modular Monolith mengambil kedisiplinan batas dari microservices tanpa menanggung biaya jaringan, penemuan layanan, dan konsistensi terdistribusi.

Jika Anda tidak bisa membangun monolit yang tertata baik, apa yang membuat Anda berpikir microservices adalah jawabannya?

Simon Brown

Kenapa lebih cepat untuk solo & tim kecil

Microservices memindahkan kompleksitas dari dalam kode ke antar layanan. Untuk tim besar dengan banyak squad, itu masuk akal — setiap squad memiliki layanannya. Tetapi untuk satu orang (atau satu orang + agent AI), biaya itu murni beban:

KebutuhanModular MonolithMicroservices
Menjalankan lokalnpm run dev, satu prosesBanyak service + orkestrasi
Transaksi lintas domainTransaksi DB biasaSaga, kompensasi, eventual consistency
Refactor batas modulPindah folder + kompiler bantuPerubahan kontrak API + versi
DeploySatu artefakPipeline per service
DebuggingSatu stack traceTracing terdistribusi

Optimasi prematur adalah akar dari segala kejahatan (atau setidaknya sebagian besarnya) dalam pemrograman.

Donald E. Knuth, 1974

Memilih microservices "untuk berjaga-jaga kalau nanti besar" adalah bentuk optimasi prematur yang paling mahal. Fowler menyebut prinsip tandingannya dengan gamblang:

Jangan mulai proyek baru dengan microservices, meski Anda yakin aplikasi Anda akan cukup besar untuk membutuhkannya. Mulailah dengan monolit — monolith first.

Martin Fowler, 2015

Struktur folder modular yang nyata

Inti disiplinnya ada di tata letak folder. Setiap domain hidup di modules/<domain> dan memuat lapisan yang seragam: schema (bentuk data & validasi), service (logika bisnis), controller (titik masuk HTTP), dan opsional events.

struktur
src/
├── modules/
│   ├── auth/
│   │   ├── auth.schema.ts        # bentuk & validasi (zod)
│   │   ├── auth.service.ts       # logika: login, hash, sesi
│   │   ├── auth.controller.ts    # route handler
│   │   └── index.ts              # API PUBLIK modul (satu-satunya pintu)
│   ├── billing/
│   │   ├── billing.schema.ts
│   │   ├── billing.service.ts
│   │   ├── billing.controller.ts
│   │   └── index.ts
│   └── invoice/
│       ├── invoice.schema.ts
│       ├── invoice.service.ts
│       └── index.ts
├── shared/                       # util lintas modul: db, config, ui-kit
│   ├── db.ts
│   └── config.ts
└── app.ts                        # rakit semua controller

Aturan batas modul (yang membuatnya tidak jadi spageti)

■ Tiga aturan batas
  1. Impor hanya lewat index.ts. Modul lain mengakses billing hanya melalui modules/billing — tidak pernah menyentuh billing.service.ts secara langsung. index.ts adalah kontrak publik.
  2. Tidak ada impor melingkar. Bila auth butuh billing dan billing butuh auth, salah satu batas Anda salah. Angkat yang bersama ke shared atau komunikasikan lewat event.
  3. Data milik satu modul. Tabel invoices hanya boleh ditulis oleh modul invoice. Modul lain memintanya lewat fungsi, bukan query langsung.
typescript
// modules/billing/index.ts — kontrak publik modul billing
export { createSubscription, cancelSubscription } from './billing.service';
export type { Subscription, Plan } from './billing.schema';
// billing.service.ts TIDAK diekspor langsung ke luar.
// Modul lain: import { createSubscription } from '@/modules/billing'

Komunikasi antar modul tanpa membuat simpul

Kadang modul billing perlu memberi tahu modul notification bahwa langganan berakhir — tetapi bila keduanya saling mengimpor, Anda menciptakan simpul yang sulit diurai. Solusinya: event. Modul memancarkan peristiwa; modul lain menanggapi tanpa saling mengenal langsung.

typescript
// shared/events.ts — bus sederhana di dalam satu proses
type Handler = (payload: any) => void | Promise<void>;
const bus = new Map<string, Handler[]>();
export const on = (e: string, h: Handler) =>
  bus.set(e, [...(bus.get(e) ?? []), h]);
export const emit = (e: string, p: any) =>
  (bus.get(e) ?? []).forEach((h) => h(p));

// billing memancarkan; ia tak tahu siapa yang mendengar
emit('subscription.ended', { tenantId });

// notification mendengar; ia tak mengimpor billing
on('subscription.ended', ({ tenantId }) => notify(tenantId, 'expired'));

Perhatikan: ketika kelak Anda memecah modul menjadi service, emit tinggal diganti pengirim pesan (queue), dan on menjadi konsumen — pola pikirnya sudah benar sejak awal. Ini contoh bagaimana Modular Monolith yang rapi menyiapkan jalan ke microservices tanpa membayarnya di muka.

Kapan naik ke microservices

Modular Monolith bukan dogma. Ada saat ia perlu dipecah — dan karena batas modul Anda sudah rapi, memecahnya jauh lebih mudah daripada memecah monolit spageti.

Karena setiap modul sudah memiliki index.ts sebagai kontrak, mengangkatnya menjadi service terpisah berarti mengganti pemanggilan fungsi dengan pemanggilan HTTP di satu tempat — bukan membedah seluruh aplikasi.

Tanda monolit Anda mulai sesak

Jangan menebak kapan harus memecah — perhatikan sinyalnya. Bila sebagian besar poin ini benar, barulah pertimbangkan mengangkat satu-dua modul menjadi service terpisah:

▲ Awas: memecah karena bosan

Godaan terbesar adalah memecah monolit karena "terasa lebih keren", bukan karena ada sinyal nyata. Setiap pemisahan menambah biaya jaringan, penerapan, dan debugging permanen. Pecah hanya ketika rasa sakit tetap-monolit melebihi biaya itu.

Peta penamaan yang konsisten

Konsistensi penamaan adalah pelumas yang membuat manusia dan AI bergerak cepat tanpa bertanya. Tetapkan sekali:

BerkasIsiContoh ekspor
*.schema.tsBentuk data + validasi (zod)InvoiceSchema, type Invoice
*.service.tsLogika bisnis murnicreateInvoice()
*.controller.tsTitik masuk HTTP/routePOST /invoices
index.tsKontrak publik modulhanya yang boleh dipakai modul lain
✦ Untuk Agent AI

Saat membuat fitur baru, selalu letakkan di modules/<domain> baru dengan empat berkas: *.schema.ts, *.service.ts, *.controller.ts, index.ts. Jangan pernah mengimpor berkas internal modul lain — impor hanya dari index.ts-nya. Bila butuh data milik modul lain, panggil fungsi yang diekspornya.

■ Rumus Bab 5

Satu deployable, banyak modul, batas via index.ts. Ini pola default. Microservices adalah pengecualian yang Anda dapatkan setelah monolit modular Anda benar-benar terlalu besar — bukan sebelum.

Bagian II

Pabrik Aplikasi

Inilah jantung buku. Bagian I memberi Anda kosakata; Bagian II memberi Anda mesin. Kita bahas dua rumus — membangun satu aplikasi dalam semalam, lalu menaikkannya menjadi sekitar tiga puluh aplikasi dalam sebulan — dilengkapi Generator Blueprint interaktif dan katalog modul siap pakai. Semua ditulis agar bisa diikuti manusia maupun dieksekusi agent AI langkah demi langkah.

Bab Enam

Rumus "1 App Semalam"

"Semalam" di sini bukan hiperbola pemasaran — ia adalah batasan yang menyehatkan. Ketika Anda memaksa diri menyelesaikan aplikasi yang berfungsi dalam satu malam, Anda otomatis membuang segala yang tidak penting. Rumus ini adalah sembilan langkah tetap. Ikuti urutannya; jangan berimprovisasi di malam pertama.

Buat ia berjalan, buat ia benar, buat ia cepat — dalam urutan itu.

Kent Beck

Prasyarat: satu keputusan yang tak bisa ditawar

Sebelum langkah pertama, kunci stack tetap Anda dan jangan pernah memperdebatkannya lagi malam itu. Buku ini memakai satu stack sebagai contoh sepanjang bab:

Next.js (App Router) PostgreSQL / Neon Prisma Auth.js / Lucia Tailwind CSS Vercel
LapisanPilihan tetapAlasan singkat
Kerangka webNext.js (App Router)Front & back dalam satu proyek; deploy sepele.
Basis dataPostgreSQL (Neon)Andal, serbaguna, serverless-ready; sama untuk SaaS & on-prem.
ORMPrismaSkema tunggal, migrasi otomatis, tipe aman.
ValidasiZodSatu sumber kebenaran untuk bentuk & validasi data.
AuthAuth.js / LuciaSesi & OAuth jadi; tak menggulung kripto sendiri.
UITailwind + ui-kitRakit cepat tanpa mendesain dari nol.
HostingVercel / DockerSaaS lewat Vercel; on-prem lewat Compose — kode sama.
◆ Tip: stack yang membosankan adalah stack yang cepat

Kecepatan datang dari keakraban, bukan dari teknologi terbaru. Pilih satu stack, kuasai betul, dan pakai untuk 30 aplikasi berikutnya. Setiap kali Anda mengganti stack, Anda membayar ulang biaya belajar.

Sembilan langkah

  1. Tentukan SATU use case sempitTulis dalam satu kalimat: "Aplikasi ini membantu [siapa] melakukan [satu hal]." Bila butuh kata "dan", buang salah satunya. Contoh: "Membantu freelancer membuat & mengirim invoice." Titik.
  2. Pilih stack tetap & scaffoldJalankan generator proyek. Jangan mulai dari halaman kosong.
    bash
    npx create-next-app@latest faktur --ts --tailwind --app
    cd faktur
    npm i prisma @prisma/client zod
    npx prisma init --datasource-provider postgresql
  3. Modul inti: skema dataModelkan domain sesempit use case. Untuk invoice: tenant, klien, invoice, item. Tidak lebih.
    prisma
    model Invoice {
      id       String   @id @default(cuid())
      tenantId String
      clientNm String
      total    Int
      status   String   @default("draft")
      items    Item[]
      @@index([tenantId])
    }
  4. AuthPasang autentikasi jadi. Jangan bikin sendiri sistem sesi dari nol.
    bash
    npm i next-auth
    # definisikan provider (email/google) di app/api/auth
  5. CRUD lewat modulBuat modules/invoice dengan schema-service-controller. Ekspor fungsi lewat index.ts.
    typescript
    // modules/invoice/invoice.service.ts
    export async function createInvoice(tenantId: string, dto: NewInvoice) {
      const data = InvoiceSchema.parse(dto);   // validasi zod
      return db.invoice.create({ data: { ...data, tenantId } });
    }
  6. UI dari ui-kitRakit halaman dari komponen siap pakai (tabel, form, tombol). Jangan mendesain dari nol; pakai Tailwind + komponen yang sudah Anda punya.
  7. Sambungkan & uji jalur utamaLogin → buat invoice → lihat daftar → ubah status. Bila jalur inti jalan, aplikasi Anda "selesai" untuk malam ini.
  8. DeployDorong ke Vercel, sambungkan Neon, atur variabel lingkungan.
    bash
    npx prisma migrate deploy
    vercel --prod      # atau push ke Git yang tersambung Vercel
  9. Selesai — dan berhentiJangan tambah fitur malam ini. Catat aplikasi ini sebagai template. Tidur. Besok mulai app berikutnya dengan resep yang sama.
▲ Awas: pembunuh "semalam"

Tiga hal yang paling sering menggagalkan target satu malam: (1) memilih stack baru untuk "coba-coba"; (2) menambah fitur kedua sebelum yang pertama jalan; (3) menyempurnakan desain UI. Semuanya adalah bentuk over-engineering. Kejar berfungsi, bukan sempurna.

Anti-pola malam pertama

Selain tiga pembunuh di atas, ada pola pikir yang diam-diam menggagalkan. Kenali agar bisa dihindari:

Anti-polaGejalaPenawar
Analysis paralysisMenghabiskan malam memilih pustaka "terbaik".Pakai stack tetap. Keputusan sudah dibuat.
Gold-platingMenyempurnakan animasi sebelum fitur jalan.Kejar "berfungsi" dulu; poles nanti bila perlu.
Scope creep"Sekalian tambah fitur X" di tengah jalan.Tulis ide di backlog; kerjakan besok sebagai app lain.
Abstraksi diniMembangun "framework" untuk satu use case.Tulis kode konkret; abstraksi lahir dari pola nyata.
Setup nerakaBerjam-jam di CI/CD sebelum ada aplikasi.Deploy manual dulu; otomasi setelah app kedua.

Perkakas: Generator Blueprint Aplikasi

Untuk mempercepat langkah 1–3, gunakan generator di bawah ini. Pilih jenis aplikasi dan fitur yang dibutuhkan; generator akan mengeluarkan daftar modul, rekomendasi model penyebaran (SaaS / On-Prem / Hybrid), dan urutan langkah membangun sebagai checklist yang bisa Anda salin — atau tempelkan ke agent AI sebagai instruksi.

⚙︎ Generator Blueprint Aplikasi

Pilih jenis app + fitur → dapatkan modul, model penyebaran, dan urutan langkah.

Pilih opsi lalu klik "Rakit Blueprint" untuk melihat modul, model penyebaran, dan langkah.
✦ Untuk Agent AI

Salin keluaran generator sebagai spesifikasi. Kerjakan modul dalam urutan yang tertera. Untuk setiap modul: buat folder modules/<nama>, tulis schema → service → controller → index, lalu lanjut. Jangan lompati langkah; jangan gabungkan dua modul dalam satu berkas.

Bab Tujuh

Rumus "30 App Sebulan"

Satu aplikasi semalam adalah keterampilan. Tiga puluh aplikasi sebulan adalah sistem. Perbedaannya bukan pada kecepatan mengetik, melainkan pada seberapa banyak yang tidak perlu Anda kerjakan ulang. Bab ini mengubah resep tunggal menjadi lini produksi.

Aritmetika yang jujur

Tiga puluh aplikasi dalam ~30 hari berarti rata-rata satu per hari kerja. Itu mustahil bila setiap aplikasi dimulai dari nol. Ia hanya mungkin bila 80% dari setiap aplikasi sudah jadi sebelum Anda mulai — datang dari template dan modul yang di-reuse. Yang tersisa hanyalah 20% yang benar-benar unik untuk use case itu.

Komposisi tiap aplikasi: 80% · Template + Modul Reuse 20% · Unik Anda hanya menulis bagian oranye. Bagian hijau sudah ada.
Gambar 7.1 — Produksi cepat = memperbesar bagian yang di-reuse, memperkecil yang ditulis ulang.

Lima pilar lini produksi

1. Starter kit / template

Sekali saja, bangun repositori template yang sudah memuat: struktur modules/, auth, ui-kit, konfigurasi Prisma, layout admin, dan skrip deploy. Setiap aplikasi baru dimulai dengan clone, bukan create.

bash
# mulai app baru dari template, bukan dari nol
npx degit myorg/starter-modular app-baru
cd app-baru && npm i
npm run setup     # skrip: rename, buat .env, migrate

2. Pustaka modul siap pakai

Modul yang berulang di banyak aplikasi — auth, billing, admin, upload, notifikasi — dipisahkan sebagai paket internal yang bisa di-install. Perbaiki bug sekali, semua aplikasi mendatang mewarisi perbaikannya. Katalognya ada di Bab 8.

3. Konvensi tetap (tak diperdebatkan ulang)

Nama folder, pola penamaan berkas, gaya penanganan error, bentuk respons API — semua ditetapkan sekali dan ditulis dalam satu dokumen CONVENTIONS.md. Konvensi yang stabil menghilangkan ratusan keputusan mikro per aplikasi, dan — penting — memberi agent AI aturan yang bisa diikuti tanpa bertanya.

4. Otomasi scaffold

Buat generator modul internal. Satu perintah menghasilkan kerangka modul lengkap sesuai konvensi.

bash
npm run gen:module -- invoice
# menghasilkan:
#   modules/invoice/invoice.schema.ts
#   modules/invoice/invoice.service.ts
#   modules/invoice/invoice.controller.ts
#   modules/invoice/index.ts

5. Checklist harian

Rutinitas yang sama setiap hari membuat kecepatan dapat diprediksi:

  1. Pilih satu use case sempit dari daftar ide.
  2. Clone template, jalankan setup.
  3. Jalankan Generator Blueprint (Bab 6) → dapatkan daftar modul & langkah.
  4. Pasang modul dari pustaka; scaffold modul unik.
  5. Tulis 20% logika unik.
  6. Uji jalur utama; deploy; catat sebagai template varian.

Rencana yang baik yang dijalankan sekarang lebih baik daripada rencana sempurna minggu depan.

George S. Patton (diadaptasi)

Musuh nomor satu: over-engineering

Setiap fitur yang Anda tambahkan "untuk berjaga-jaga" adalah pajak yang Anda bayar di setiap aplikasi berikutnya. Disiplin "30 app" menuntut penolakan aktif:

▲ Daftar larang di lini produksi
  • Jangan pasang microservices sebelum monolit terasa sesak (Bab 5).
  • Jangan bangun abstraksi untuk kasus yang belum ada.
  • Jangan tulis konfigurasi yang bisa diubah bila belum ada yang memintanya.
  • Jangan optimasi performa sebelum ada pengguna yang mengeluh.

Metrik lini produksi

Yang tak diukur tak bisa dipercepat. Pantau beberapa angka sederhana agar tahu apakah "pabrik" Anda benar-benar berproduksi atau hanya sibuk:

Kesalahan umum saat menskalakan produksi

KesalahanAkibatPerbaikan
Template dibiarkan basiSetiap app mewarisi bug lamaRawat template seperti produk; perbarui rutin
Modul di-copy antar repoPerbaikan tak menyebarJadikan paket bersama
Konvensi berbeda tiap appBeban mental & AI bingungSatu CONVENTIONS.md, ditegakkan lint
Tak pernah membuangMenyeret app mati selamanyaPensiunkan app tak terpakai tanpa sentimen
■ Rumus Bab 7

Reuse > rewrite. Konvensi > kreativitas. Template > nol. Kecepatan 30 app/bulan bukan soal bekerja lebih keras, melainkan tidak mengerjakan yang sama dua kali.

Bab Delapan

Katalog Modul Siap Pakai

Ini adalah rak gudang lini produksi Anda. Sembilan modul yang muncul di hampir setiap aplikasi. Untuk masing-masing: apa fungsinya, kapan dipakai, dan cara pasang ringkasnya. Bangun sekali, reuse selamanya.

🔐

Auth

Login, sesi, reset kata sandi.

💳

Billing

Langganan, webhook, invoice.

🛠️

Admin Panel

CRUD data + RBAC.

📤

Upload / Media

Unggah berkas ke S3/R2.

🔔

Notifikasi

Email + antrian kejadian.

📜

Audit Log

Jejak tindakan tak terhapus.

🗝️

API Key / License

Kunci akses & aktivasi.

🏢

Multi-Tenant

Isolasi data antar pelanggan.

🚩

Feature Flags

Nyala/mati fitur per plan.

1. Auth

Apa: registrasi, login, sesi, lupa kata sandi, dan (opsional) OAuth. Kapan: hampir selalu — kecuali alat publik tanpa akun. Pasang: gunakan pustaka jadi (Auth.js/Lucia); jangan gulung kriptografi sendiri. Ekspos getSession() dan requireUser() dari modules/auth.

2. Billing / Langganan

Apa: menghubungkan aplikasi ke penyedia pembayaran, memetakan status langganan tenant, dan membuka/mengunci fitur. Kapan: untuk SaaS berbayar. Pasang: satu endpoint webhook + fungsi getPlan(tenantId). Kombinasikan dengan Feature Flags untuk mengunci fitur.

3. Admin Panel

Apa: antarmuka internal untuk mengelola data & pengguna, dilindungi RBAC. Kapan: begitu ada data yang perlu dikelola manusia. Pasang: generator CRUD di atas skema Prisma; batasi dengan peran admin.

4. Upload / Media

Apa: unggah berkas, hasilkan URL, opsional ubah ukuran gambar. Kapan: aplikasi dengan foto/dokumen. Pasang: simpan ke object storage (S3/Cloudflare R2) via presigned URL; simpan hanya metadata di DB.

typescript
// modules/storage/index.ts
export async function presignUpload(tenantId: string, name: string) {
  const key = `${tenantId}/${crypto.randomUUID()}-${name}`;
  return s3.presign(key, { expiresIn: 300 }); // 5 menit
}

5. Notifikasi

Apa: mengirim email/notifikasi saat kejadian penting, lewat antrian agar tak memblokir permintaan. Kapan: onboarding, reset kata sandi, peringatan billing. Pasang: penyedia email (Resend/SES) + antrian (Upstash QStash). Modul lain cukup memanggil notify(event).

6. Audit Log

Apa: catatan siapa melakukan apa dan kapan, hanya bisa ditambah (append-only). Kapan: aplikasi B2B, on-prem, atau apa pun dengan tuntutan kepatuhan. Pasang: satu tabel audit + fungsi audit(actor, action, target) yang dipanggil dari service.

7. API Key / License Manager

Apa: menerbitkan & memvalidasi kunci — untuk API publik (SaaS) maupun aktivasi lisensi (on-prem). Kapan: ada integrasi eksternal atau distribusi on-prem. Pasang: kunci di-hash saat disimpan; validasi via middleware. Detail lisensi di Bab 9.

8. Multi-Tenant

Apa: memastikan setiap query terikat pada tenantId yang benar. Kapan: setiap SaaS. Pasang: bungkus akses DB dengan konteks tenant sehingga tenantId disuntik otomatis — bukan diingat manual (lihat Bab 11).

9. Feature Flags

Apa: menyalakan/mematikan fitur per tenant, per plan, atau untuk rilis bertahap. Kapan: begitu ada tingkatan langganan atau fitur eksperimental. Pasang: fungsi flag(tenant, 'nama') yang membaca dari plan + override. Ringan, tetapi mengubah cara Anda merilis.

Peta cepat: modul mana untuk kebutuhan apa

Butuh…Pasang modulWajib untuk model
Pengguna loginauth, sessionSemua
Menerima pembayaran berulangbilling, invoice, webhookSaaS, Hybrid
Mengelola data internaladmin-panel, rbacSemua
Menyimpan berkas/gambarstorage, media-pipelineSemua
Memberi tahu penggunanotification, email, queueSemua
Melacak perubahan pentingaudit-logOn-Prem, B2B
Distribusi berlisensilicense-manager, offline-activationOn-Prem, Hybrid
Melayani banyak pelangganmulti-tenant, tenant-routerSaaS, Marketplace
Buka/tutup fitur per planfeature-flagsSaaS berjenjang

Modul dalam praktik: satu folder utuh

Agar konkret, inilah wujud lengkap modul billing dalam struktur folder — cetakan yang sama untuk setiap modul lain.

struktur
modules/billing/
├── billing.schema.ts     # Plan, Subscription, event webhook (zod)
├── billing.service.ts    # createSubscription, cancel, getPlan
├── billing.controller.ts # POST /webhook, GET /billing/portal
├── billing.events.ts     # reaksi atas event: buka/kunci fitur
└── index.ts              # ekspor: getPlan, createSubscription, tipe Plan
◆ Tip: modul = paket, bukan salin-tempel

Jangan menyalin folder modul antar proyek dengan copy-paste — perbaikan bug tak akan menyebar. Terbitkan modul sebagai paket internal (npm privat / workspace monorepo) agar semua aplikasi menarik dari sumber yang sama.

Bagian III

Operasi

Kecepatan tanpa operasi yang matang hanya menghasilkan prototipe. Bagian ini membahas tiga hal yang mengubah aplikasi menjadi produk yang bisa dijual, dipasang, dan dipercaya: lisensi & distribusi, deployment untuk ketiga model, serta keamanan & data.

Bab Sembilan

Lisensi & Distribusi

Bagaimana Anda dibayar? Pertanyaan ini sepele di SaaS (matikan akun bila tak bayar) tetapi rumit di on-prem (aplikasinya di server orang lain). License Manager adalah jawaban teknis untuk pertanyaan komersial itu, dan ia relevan untuk ketiga model.

Anatomi sebuah lisensi

Sebuah lisensi pada dasarnya adalah klaim bertanda tangan: pernyataan tentang siapa yang berhak, sampai kapan, dan untuk apa — yang kebenarannya bisa diverifikasi secara kriptografis tanpa mempercayai pihak yang memegangnya.

json
{
  "licenseId": "lic_8fa2...",
  "customer": "PT Sehat Sentosa",
  "plan": "enterprise",
  "seats": 50,
  "features": ["audit", "sso", "api"],
  "issuedAt": "2026-01-01",
  "expiresAt": "2027-01-01",
  "signature": "ed25519:9c3d..."   // tanda tangan atas seluruh isi
}

Kunci publik/privat: kenapa aman

Anda menandatangani lisensi dengan kunci privat yang hanya Anda miliki. Aplikasi memverifikasinya dengan kunci publik yang boleh tertanam di dalam kode. Karena hanya kunci privat yang bisa membuat tanda tangan sah, klien tak bisa memalsukan atau mengubah lisensi — mengubah satu huruf pun membatalkan tanda tangan.

typescript
// modules/license/verify.ts — verifikasi offline, tanpa internet
import { verify } from '@noble/ed25519';
export async function isValid(lic: License): Promise<boolean> {
  const body = canonical(lic);              // tanpa field signature
  const ok = await verify(lic.signature, body, PUBLIC_KEY);
  return ok && new Date(lic.expiresAt) > new Date();
}

Online vs offline: dua mode aktivasi

AspekAktivasi OnlineAktivasi Offline
CaraAplikasi menelepon license server berkalaKlien menempel berkas lisensi bertanda tangan
Butuh internetYa (koneksi keluar)Tidak — cocok air-gapped
Cabut lisensiInstan (server berhenti memperpanjang)Menunggu kedaluwarsa berkas
Penagihan pemakaianMudah (kirim metrik saat menelepon)Perlu ekspor manual
◆ Tip: dukung keduanya

Bangun License Manager yang online secara default, offline sebagai cadangan. Bila license server terjangkau, gunakan untuk perpanjangan & metrik; bila tidak (air-gapped), jatuh ke verifikasi berkas offline. Satu modul, dua mode.

Daur hidup lisensi

Lisensi bukan sakelar biner "sah/tidak". Ia bergerak melalui beberapa keadaan, dan aplikasi Anda harus menanganinya dengan anggun — terutama masa tenggang, yang menyelamatkan hubungan pelanggan saat jaringan sesaat terganggu.

Terbit Aktif Tenggang(grace) Terbatas Diperpanjang gagal validasi → tenggang; pulih → aktif kembali
Gambar 9.1 — Keadaan lisensi: gagal validasi masuk masa tenggang dulu, bukan langsung terbatas.

Penagihan & proteksi

■ Rumus Bab 9

Lisensi = klaim bertanda tangan. Tanda tangani dengan kunci privat, verifikasi dengan kunci publik, dukung online & offline, dan selalu beri masa tenggang.

Bab Sepuluh

Deployment

Karena kode Anda satu Modular Monolith, deployment menjadi soal pengemasan, bukan penulisan ulang. Bab ini menyajikan resep konkret untuk masing-masing dari tiga model. Semua berangkat dari basis kode yang sama.

Jalur SaaS: Vercel + Neon + Upstash

Model tercepat untuk rilis. Aplikasi ke Vercel, database Postgres serverless ke Neon, cache/antrian ke Upstash.

bash
# variabel lingkungan inti
DATABASE_URL=postgres://...neon.tech/app
REDIS_URL=rediss://...upstash.io
AUTH_SECRET=...

# rilis
npx prisma migrate deploy
vercel --prod
◆ Tip: VPS sebagai alternatif

Bila Anda ingin biaya tetap & kendali lebih, jalankan Modular Monolith yang sama di satu VPS (mis. dengan Docker + Caddy). Karena satu proses, satu VPS cukup untuk ribuan pengguna awal.

Jalur On-Premise: Docker Compose ke server klien

Kemas seluruh sistem menjadi satu berkas yang bisa dijalankan klien dengan satu perintah.

docker-compose.yml
services:
  app:
    image: registry.myorg.io/faktur:1.4.0
    env_file: .env
    depends_on: [db]
    ports: ["3000:3000"]
  db:
    image: postgres:16
    volumes: ["pgdata:/var/lib/postgresql/data"]
    environment:
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
  worker:
    image: registry.myorg.io/faktur:1.4.0
    command: ["node", "worker.js"]
    depends_on: [db]
volumes: { pgdata: {} }
bash · di server klien
docker compose pull
docker compose run --rm app npx prisma migrate deploy
docker compose up -d      # selesai — aplikasi jalan di gedung klien

Jalur Hybrid: inti on-prem + license/sync server di cloud

Gabungan keduanya. Aplikasi (Compose) berjalan di klien; sebuah layanan kecil di cloud menangani lisensi & sinkronisasi. Aplikasi klien memegang LICENSE_SERVER_URL dan menelepon berkala.

env · sisi klien
DEPLOY_MODE=hybrid
LICENSE_SERVER_URL=https://lic.myorg.io
LICENSE_KEY=lic_8fa2...
SYNC_INTERVAL=6h          # kirim metrik agregat tiap 6 jam

Observabilitas: tahu saat rusak

Aplikasi yang di-deploy tanpa mata adalah aplikasi yang gagal diam-diam. Tiga lapisan minimum, berlaku untuk ketiga model:

typescript
// app/api/health/route.ts — cukup untuk ketiga model
export async function GET() {
  const db = await ping().then(() => 'ok').catch(() => 'down');
  const ok = db === 'ok';
  return Response.json({ status: ok ? 'ok' : 'degraded', db }, { status: ok ? 200 : 503 });
}

Satu kode, tiga mode: saklar konfigurasi

Perbedaan antar model idealnya hanya konfigurasi, bukan cabang kode yang berserakan. Pusatkan di satu tempat.

typescript
// shared/config.ts — satu saklar untuk tiga model
export const mode = process.env.DEPLOY_MODE ?? 'saas';
export const features = {
  centralBilling: mode === 'saas' || mode === 'hybrid',
  localData:      mode === 'onprem' || mode === 'hybrid',
  licenseCheck:   mode !== 'saas',
};

Ekonomi tiga jalur

Biaya bukan sekadar angka server; ia mencakup operasional, dukungan, dan waktu Anda. Gambaran kasar untuk membantu memilih:

FaktorSaaS (Vercel/serverless)VPS tunggalOn-Prem (per klien)
Biaya awalNyaris nolRendah & tetapTinggi (instalasi, pelatihan)
Biaya jalanTumbuh dengan pemakaianTetap, dapat diprediksiDitanggung klien
Beban operasi AndaRendah (dikelola)SedangTinggi (dukungan multi-versi)
SkalaOtomatisManual (naikkan spek)Per instalasi
Cocok saatTrafik naik-turun, ingin cepatBeban stabil, hematRegulasi/enterprise
◆ Tip: jangan takut VPS

Untuk produk baru dengan beban stabil, satu VPS berukuran wajar menjalankan Modular Monolith Anda dengan biaya tetap yang jauh lebih murah daripada tagihan serverless yang membengkak. Karena kodenya sama, Anda bisa pindah dari Vercel ke VPS (atau sebaliknya) tanpa menulis ulang.

■ Rumus Bab 10

Deploy = konfigurasi, bukan penulisan ulang. Satu Modular Monolith + satu berkas config + satu Docker Compose = kesiapan untuk SaaS, on-prem, dan hybrid sekaligus.

Bab Sebelas

Keamanan & Data

Keamanan bukan modul yang dipasang di akhir; ia adalah cara Anda menata akses data sejak awal. Bab ini memadatkan praktik yang paling sering menyelamatkan — dan yang paling sering dilupakan dalam kejaran kecepatan.

Isolasi tenant yang tak bisa dilupakan

Kesalahan paling mahal di SaaS adalah kebocoran antar tenant. Cara mencegahnya bukan dengan mengingat menambahkan where tenantId di setiap query — manusia dan AI sama-sama lupa. Caranya adalah membuat lupa menjadi mustahil: bungkus akses data dalam konteks tenant.

typescript
// shared/db.ts — tenantId disuntik otomatis, bukan diingat
export function forTenant(tenantId: string) {
  return prisma.$extends({
    query: {
      $allModels: {
        async $allOperations({ args, query }) {
          args.where = { ...args.where, tenantId }; // paksa filter
          return query(args);
        },
      },
    },
  });
}
// Pemakaian: const db = forTenant(session.tenantId);

RBAC: siapa boleh apa

Setelah memastikan data terikat tenant yang benar, lapisan berikutnya adalah peran: di dalam satu tenant, tidak semua pengguna setara. Model paling sederhana yang cukup untuk mayoritas aplikasi adalah peran berbasis daftar izin.

typescript
// modules/rbac/policy.ts — cukup untuk sebagian besar app
const POLICY = {
  owner:  ['*'],                          // semua izin
  admin:  ['invoice:*', 'user:read'],
  member: ['invoice:read', 'invoice:create'],
} as const;

export function can(role: Role, perm: string): boolean {
  const grants = POLICY[role] ?? [];
  return grants.includes('*') || grants.some((g) =>
    g === perm || (g.endsWith(':*') && perm.startsWith(g.slice(0, -1))));
}
◆ Tip: jangan mulai dari RBAC penuh

Untuk 1 app semalam, tiga peran (owner/admin/member) hampir selalu cukup. Sistem izin dinamis yang bisa dikonfigurasi per tenant adalah fitur yang Anda tambahkan ketika ada pelanggan yang membayar untuknya — bukan sebelumnya.

Enkripsi & masking PII

typescript
export const maskPhone = (p: string) =>
  p.replace(/^(\d{4})\d+(\d{4})$/, '$1****$2');

Backup & pemulihan

Backup yang tak pernah diuji sama saja tidak ada. Untuk SaaS, andalkan backup terkelola penyedia + uji restore berkala. Untuk on-prem, sediakan skrip backup yang mudah dijalankan klien dan dokumentasi restore yang jelas — pada on-prem, backup adalah tanggung jawab klien, tetapi kegagalannya akan menjadi masalah reputasi Anda.

Kepatuhan tanpa drama

Kepatuhan (mis. UU PDP di Indonesia, GDPR) terdengar menakutkan, tetapi tiga praktik menutup sebagian besar kebutuhan: (1) audit log yang utuh; (2) kemampuan menghapus/mengekspor data satu pengguna; (3) minimisasi data — jangan kumpulkan yang tak Anda butuhkan.

Amdahl memberi kita hukum tentang batas percepatan; keamanan mengajarkan hukum serupa: rantai sekuat mata rantai terlemahnya.

Prinsip rekayasa umum

Pembagian tanggung jawab keamanan

Siapa menjaga apa berbeda per model. Menyadari batas ini mencegah asumsi berbahaya — mis. mengira klien on-prem sudah mengurus backup padahal tidak.

Tanggung jawabSaaSOn-PremHybrid
Keamanan aplikasi (kode)AndaAndaAnda
Patch infrastruktur/OSAndaKlienTerbagi
Backup dataAndaKlienKlien (data) + Anda (kendali)
Kontrol akses fisikPenyedia cloudKlienKlien
Validasi lisensiAndaAnda
▲ Awas: kecepatan bukan alasan mengabaikan ini

Isolasi tenant, HTTPS, hashing kata sandi, dan audit log bukan "fitur nanti". Semuanya sudah ada di modul siap pakai Anda (Bab 8). Memasangnya di malam pertama tidak memperlambat — melupakannya-lah yang kelak menghancurkan.

■ Rumus Bab 11

Buat kesalahan menjadi mustahil, bukan sekadar terlarang. Suntik tenantId otomatis, enkripsi secara default, masking PII, dan audit segalanya. Keamanan yang tertanam di lapisan akses tidak bisa dilupakan oleh manusia maupun AI.

Bagian IV

Penerapan

Teori sudah cukup. Bagian penutup ini menerapkan seluruh buku pada skenario nyata, menunjukkan bagaimana agent AI mengeksekusi resep-resepnya, lalu menutup dengan glosarium, checklist induk, dan tanya-jawab.

Bab Dua Belas

Studi Kasus

Lima skenario, lima blueprint. Untuk masing-masing kita tentukan use case, modul, model penyebaran, dan alasannya. Perhatikan bahwa keputusan penyebaran selalu datang terakhir, mengikuti kebutuhan pelanggan — bukan selera teknis.

Kasus 1 — SaaS kecil: "Faktur" untuk freelancer

Use case: freelancer membuat & mengirim invoice. Model: SaaS. Alasan: banyak pengguna kecil, data boleh di cloud, langganan bulanan. Deploy Vercel + Neon. Malam pertama cukup untuk versi yang berfungsi.

authmulti-tenant (pool)invoicebillingnotifikasi

Langkah unik malam itu: modelkan invoice & item → CRUD invoice → tombol "kirim" (email PDF) → halaman daftar dengan status. Selesai.

Kasus 2 — Internal tool: dashboard operasi gudang

Use case: staf gudang mencatat stok masuk/keluar. Model: On-Premise (atau VPS internal). Alasan: satu organisasi, data operasional internal, tim IT sendiri. Tanpa API publik, hybrid tak diperlukan. Kemas Docker Compose.

authrbacadmin-panelstockaudit-log

Catatan: karena internal, lewati billing sepenuhnya. Fokus pada RBAC (siapa boleh apa) dan audit log (siapa mengubah stok). Tanpa fitur yang tak dipakai.

Kasus 3 — Produk on-prem enterprise: SIM rumah sakit

Use case: sistem informasi manajemen klinik/rumah sakit. Model: Hybrid. Alasan: data pasien wajib tinggal di rumah sakit (regulasi), tetapi vendor ingin lisensi & pembaruan terpusat. Inti on-prem, license/sync server di cloud, dengan masa tenggang panjang untuk jaringan rumah sakit yang kerap terisolasi.

authrbacaudit-loglicense-manageroffline-activationdomain-medis

Kunci desain: masa tenggang panjang (mis. 30 hari) karena jaringan rumah sakit sering terisolasi; sinkronisasi hanya metrik agregat (jumlah seat), bukan data pasien; aktivasi offline via berkas bertanda tangan untuk instalasi air-gapped.

Kasus 4 — AI app: asisten ringkasan dokumen

Use case: pengguna mengunggah dokumen, mendapat ringkasan. Modul: auth, upload/storage, ai-provider, prompt-registry, usage-meter, billing. Model: SaaS (usage-based). Alasan: butuh GPU/model di cloud, ditagih per token. Kunci: abstraksi ai-provider agar bisa berganti model tanpa menyentuh sisa aplikasi, dan usage-meter untuk penagihan akurat.

authstorageai-providerprompt-registryusage-meterbilling
◆ Tip AI app

Selalu bungkus penyedia model di balik satu antarmuka modules/ai-provider. Harga, kecepatan, dan kualitas model berubah cepat; abstraksi ini adalah asuransi Anda agar bisa berpindah tanpa menulis ulang aplikasi.

Kasus 5 — Marketplace: platform jasa lokal

Use case: mempertemukan penyedia jasa dan pelanggan. Model: SaaS. Alasan: dua sisi pengguna, transaksi & pembagian dana, butuh iterasi cepat. Marketplace adalah yang paling padat modul — justru di sinilah katalog modul siap pakai paling menghemat.

authmulti-tenantcatalogorderpaymentspayoutnotifikasiadmin

Peringatan scope: marketplace mudah membengkak. Untuk malam pertama, kirim versi satu-sisi dulu (mis. hanya penyedia mengunggah jasa + pelanggan memesan), lalu tambah pembayaran & payout di iterasi berikut. Jangan bangun semuanya sekaligus.

KasusModelPemicu keputusan
Faktur freelancerSaaSBanyak pengguna kecil, data boleh di cloud
Dashboard gudangOn-PremInternal, satu organisasi
SIM rumah sakitHybridData lokal + lisensi terpusat
Asisten AISaaSModel di cloud, tagih per pemakaian
Marketplace jasaSaaSDua sisi, transaksi, iterasi cepat

Satu produk menaiki tangga penyebaran

Studi kasus di atas terlihat seolah setiap produk memilih satu model selamanya. Kenyataannya, produk yang sukses sering menaiki tangga: mulai sebagai SaaS, lalu menambahkan opsi on-prem/hybrid saat pelanggan enterprise datang. Karena Modular Monolith, tangga ini didaki tanpa menulis ulang.

  1. Mulai: SaaS poolRilis cepat, pelanggan kecil, satu instans multi-tenant. Validasi ide dengan biaya terendah.
  2. Tumbuh: tambah tingkatanFeature flags membuka Pro/Business. Sebagian tenant naik ke bridge untuk isolasi lebih.
  3. Enterprise datang: hidupkan hybridNyalakan DEPLOY_MODE=hybrid + License Manager. Inti berjalan di klien, kendali tetap di cloud.
  4. Regulasi ketat: on-prem penuhKemas Docker Compose + aktivasi offline untuk klien air-gapped. Kode yang sama, kemasan berbeda.
■ Rumus Bab 12

Pilih model per pelanggan, bukan per produk. Satu Modular Monolith melayani seluruh tangga — dari startup yang mencoba hingga enterprise yang teregulasi — tanpa pernah bercabang menjadi dua basis kode.

Bab Tiga Belas

Cara Buku Ini Dibaca AI

Buku ini sengaja ditulis dua arah: nyaman dibaca manusia, dan terstruktur untuk dieksekusi agent AI. Bab ini menjelaskan cara kerja "resep" agar sebuah agent dapat membangun aplikasi bertahap dari halaman-halaman sebelumnya.

Kenapa struktur ini ramah-AI

Protokol eksekusi bertahap

Agar sebuah agent membangun aplikasi dari buku ini, ia mengikuti lingkaran kecil yang sama untuk tiap langkah:

  1. Ambil satu langkahBaca satu item dari resep (mis. langkah 5 Bab 6: "CRUD lewat modul"). Jangan gabungkan beberapa langkah.
  2. Periksa prasyaratPastikan langkah sebelumnya selesai (mis. skema data sudah ada sebelum menulis service).
  3. Eksekusi sesuai konvensiIkuti aturan batas modul Bab 5: buat schema → service → controller → index. Jangan menyimpang dari konvensi.
  4. Verifikasi kriteria selesaiJalankan jalur uji yang disebut resep (mis. "login → buat invoice → lihat daftar"). Bila gagal, perbaiki sebelum lanjut.
  5. Lanjut ke langkah berikutBaru setelah langkah ini benar-benar selesai.
✦ Untuk Agent AI — kontrak eksekusi
  1. Perlakukan Generator Blueprint (Bab 6) sebagai spesifikasi awal: jenis app + fitur → daftar modul + langkah.
  2. Bangun modul dalam urutan yang tertera. Satu modul = satu folder modules/<nama> dengan empat berkas.
  3. Patuhi tiga aturan batas (Bab 5): impor via index.ts, tanpa siklus, data milik satu modul.
  4. Terapkan keamanan default (Bab 11): suntik tenantId otomatis, hash kata sandi, audit.
  5. Tunda keputusan penyebaran hingga akhir; gunakan saklar DEPLOY_MODE (Bab 10).
  6. Berhenti pada definisi "selesai" use case. Jangan menambah fitur yang tak diminta.

Contoh prompt untuk memulai

prompt
Bangun aplikasi mengikuti blueprint berikut (dari Generator Bab 6):

Jenis: SaaS Multi-Tenant
Fitur: auth, admin, billing
Model penyebaran: SaaS

Aturan:
- Arsitektur Modular Monolith (Bab 5): modules/<domain>
  dengan schema, service, controller, index.
- Impor antar modul HANYA lewat index.ts.
- Multi-tenant pola pool; suntik tenantId otomatis (Bab 11).
- Kerjakan satu modul sampai selesai sebelum lanjut.
- Berhenti saat jalur utama berjalan. Jangan tambah fitur.
Bab Empat Belas

Penutup

Kita kembali ke pertanyaan pembuka: SaaS atau on-premise? Setelah empat belas bab, jawabannya seharusnya terasa berbeda. Bukan "pilih salah satu", melainkan "bangun sesuatu yang tidak memaksa Anda memilih terlalu dini".

Modular Monolith adalah pusat gravitasi buku ini karena ia membebaskan Anda dari keputusan mahal di awal. Ia membuat "1 app semalam" mungkin, membuat "30 app sebulan" berkelanjutan, dan membuat model penyebaran menjadi sekadar saklar konfigurasi. SaaS, on-prem, dan hybrid bukan lagi tiga jalan berbeda, melainkan tiga tujuan dari satu jalan yang sama.

Cara terbaik memprediksi masa depan adalah menciptakannya.

Alan Kay

Maka mulailah. Pilih satu use case sempit malam ini. Clone template. Rakit modul. Kirim. Besok, ulangi. Biarkan disiplin — bukan drama arsitektur — yang menentukan seberapa banyak yang Anda kirim.

Untuk vibe-coder & agent-nya

Buku ini lahir dari keyakinan bahwa membangun perangkat lunak seharusnya terasa seperti merakit, bukan menuang beton. Ketika Anda punya blok-blok modul yang tepat dan jalur perakitan yang jelas, membuat aplikasi menjadi tindakan yang bisa diulang dengan tenang — malam demi malam, tanpa kepanikan arsitektur.

Bagi Anda yang bekerja bersama agent AI: buku ini adalah bahasa bersama antara Anda dan asisten Anda. Ia memberi agent aturan yang cukup untuk bergerak cepat tanpa tersesat, dan memberi Anda kerangka untuk mengevaluasi apa yang dihasilkannya. Manusia menetapkan arah & scope; agent mengeksekusi resep; buku ini menjaga keduanya sejalan.

Program harus ditulis agar dibaca manusia, dan hanya kebetulan agar dijalankan mesin.

Harold Abelson & Gerald Jay Sussman, SICP

Kalimat itu, ditulis puluhan tahun lalu, kini punya makna baru: program — dan buku tentang program — sebaiknya bisa dibaca manusia maupun agent AI. Semoga buku ini membuktikannya.

Glosarium

SaaS (Software as a Service)
Perangkat lunak yang berjalan di server pembuatnya dan disewa pelanggan lewat langganan; diakses via peramban.
On-Premise (On-Prem)
Perangkat lunak yang dipasang dan berjalan di server milik pelanggan sendiri, di dalam infrastruktur mereka.
Hybrid
Kombinasi: sebagian aplikasi/data di sisi klien (on-prem), sebagian layanan (lisensi, billing, sinkronisasi, AI) di cloud.
Multi-Tenancy
Kemampuan satu aplikasi melayani banyak pelanggan (tenant) dengan data yang terisolasi antar mereka.
Tenant
Satu unit pelanggan (perusahaan/tim/akun) yang datanya terpisah dari tenant lain dalam SaaS.
Pool / Silo / Bridge
Tiga pola isolasi tenant: berbagi tabel dengan tenantId (pool), database terpisah per tenant (silo), atau skema terpisah (bridge).
Modular Monolith
Satu aplikasi (satu deployable) yang dibagi menjadi modul-modul berbatas tegas yang berkomunikasi lewat pemanggilan fungsi, bukan jaringan.
Microservices
Arsitektur banyak layanan kecil terpisah yang berkomunikasi lewat jaringan; kuat di skala besar, mahal untuk tim kecil.
Modul
Unit kode berbatas untuk satu domain; berisi schema, service, controller, dan kontrak publik (index.ts).
License Manager
Mekanisme menerbitkan & memvalidasi lisensi (online/offline) agar distribusi on-prem & hybrid dapat ditagih dan dilindungi.
Aktivasi Offline / Air-gapped
Validasi lisensi tanpa internet, memakai berkas bertanda tangan kriptografis; wajib untuk lingkungan tanpa koneksi keluar.
Feature Flag
Saklar untuk menyalakan/mematikan fitur per tenant, plan, atau rilis bertahap.
Webhook
Panggilan HTTP yang dikirim penyedia (mis. pembayaran) ke aplikasi Anda saat suatu peristiwa terjadi.
PII
Personally Identifiable Information — data yang mengidentifikasi individu (nama, NIK, telepon); wajib dilindungi & disamarkan.
Idempoten
Sifat operasi yang aman diulang: menjalankannya berkali-kali menghasilkan efek yang sama seperti sekali.
Over-engineering
Membangun kompleksitas untuk kebutuhan yang belum ada; musuh utama kecepatan produksi.
Docker Compose
Berkas yang mendefinisikan beberapa kontainer (aplikasi, database, worker) sebagai satu kesatuan; cara utama mengemas on-prem.
Presigned URL
URL sementara bertanda tangan yang mengizinkan unggah/unduh berkas langsung ke object storage tanpa melewati server aplikasi.
RBAC
Role-Based Access Control — pengaturan izin berdasarkan peran pengguna (mis. owner, admin, member).
Vibe-coder
Sebutan santai untuk pembangun yang mengalir cepat dari ide ke produk, sering dibantu agent AI, dengan menekankan momentum & iterasi.
Grace period (masa tenggang)
Jendela waktu saat lisensi gagal divalidasi tetapi aplikasi tetap berjalan penuh, mencegah gangguan akibat jaringan sesaat.

Checklist Induk

◆ Checklist "1 App Semalam"
  • ☐ Use case ditulis dalam satu kalimat tanpa kata "dan".
  • ☐ Scaffold dari stack tetap (bukan halaman kosong).
  • ☐ Skema data seminimal use case.
  • ☐ Auth dari pustaka jadi.
  • ☐ Modul domain: schema → service → controller → index.
  • ☐ UI dirakit dari ui-kit.
  • ☐ Jalur utama diuji (login → aksi inti → lihat hasil).
  • ☐ Deploy (Vercel/Compose sesuai model).
  • ☐ Dicatat sebagai template. Berhenti menambah fitur.
■ Checklist "Sistem 30 App"
  • ☐ Starter kit/template tersedia & teruji.
  • ☐ Modul siap pakai jadi paket, bukan salin-tempel.
  • CONVENTIONS.md ditulis & dipatuhi.
  • ☐ Generator scaffold modul berjalan.
  • ☐ Checklist harian dijalankan konsisten.
  • ☐ Larangan over-engineering ditegakkan.
✦ Checklist Kesiapan Penyebaran
  • ☐ Saklar DEPLOY_MODE menentukan SaaS/On-Prem/Hybrid.
  • ☐ Isolasi tenant otomatis (bukan manual).
  • ☐ License Manager mendukung online & offline.
  • ☐ Docker Compose siap untuk on-prem.
  • ☐ Backup & restore terdokumentasi.
  • ☐ Audit log & masking PII aktif.

FAQ

Bukankah monolit itu ketinggalan zaman?
Tidak. Yang ketinggalan zaman adalah monolit spageti tanpa batas. Modular Monolith mengambil disiplin batas dari microservices tanpa biaya jaringannya. Banyak perusahaan besar justru kembali ke monolit modular karena microservices terlalu mahal untuk sebagian besar tim.
Apakah "1 app semalam" realistis atau sekadar slogan?
Realistis untuk aplikasi ber-scope sempit di atas stack yang Anda kuasai dan template yang di-reuse. Yang tidak realistis adalah aplikasi kompleks multi-fitur dari nol. Rumusnya justru memaksa Anda mempersempit scope hingga muat dalam semalam.
Kapan saya benar-benar butuh microservices?
Ketika beberapa tim saling menabrak di satu basis kode, ketika satu modul butuh sumber daya/rilis yang sangat berbeda, atau ketika beban satu bagian jauh melampaui sisanya. Karena batas modul Anda sudah rapi, memecahnya nanti jauh lebih murah daripada memulai dengan microservices sekarang.
Bagaimana memilih antara pool, silo, dan bridge?
Mulai dari pool — termurah & tercepat. Naikkan tenant tertentu ke bridge/silo hanya ketika ada pelanggan yang membayar untuk isolasi lebih kuat. Jangan bangun silo untuk semua tenant sejak awal.
Apakah hybrid tidak terlalu rumit untuk tim kecil?
Hybrid rumit bila Anda menyinkronkan banyak data mentah dua arah. Bila Anda membatasinya pada pola "inti on-prem + license/sync server kecil di cloud" dengan sinkron satu arah & idempoten, kompleksitasnya terkendali — dan seringkali itulah yang benar-benar dibutuhkan klien enterprise.
Apakah saya harus memakai stack Next.js + Postgres persis?
Tidak — yang penting adalah menetapkan satu stack dan konsisten memakainya. Next.js + Postgres + Prisma dipakai di buku ini karena serbaguna & sama-sama mudah untuk SaaS maupun on-prem. Bila Anda lebih cepat dengan stack lain, pakai itu; hanya jangan berganti-ganti tiap aplikasi.
Bagaimana kalau aplikasi saya harus SaaS sekaligus on-prem?
Itu justru skenario yang buku ini rancang. Karena kodenya satu Modular Monolith dengan saklar DEPLOY_MODE (Bab 10), aplikasi yang sama bisa dijalankan sebagai SaaS untuk pelanggan kecil dan dikemas Docker Compose untuk pelanggan enterprise on-prem — tanpa dua basis kode.
Bagaimana buku ini membantu agent AI saya?
Setiap resep ditulis sebagai langkah eksplisit dengan kriteria selesai, konvensi tetap, dan blok kode yang bisa disalin. Gunakan Generator Blueprint (Bab 6) sebagai spesifikasi, lalu minta agent mengeksekusi per langkah mengikuti kontrak di Bab 13.
GP
Tentang Penulis
Galih Prasetyo

Praktisi perangkat lunak yang menghabiskan bertahun-tahun membangun produk untuk beragam model penyebaran — dari SaaS multi-tenant hingga sistem on-premise enterprise. Ia percaya bahwa kecepatan yang berkelanjutan lahir dari disiplin arsitektur, bukan dari mengejar teknologi terbaru.

Buku ini adalah rangkuman rumusnya: satu basis kode Modular Monolith, katalog modul yang di-reuse, dan penolakan keras terhadap over-engineering — cukup eksplisit untuk diikuti manusia, cukup terstruktur untuk dieksekusi agent AI.

↑ Awal Buku Generator Blueprint