Database relasional mengharuskan struktur didefinisikan lebih dulu: nama kolom, tipe data, dan batasan NOT NULL ditetapkan sebelum satu baris data pun masuk. MongoDB membalik urutan itu — sebuah collection bisa langsung dipakai tanpa definisi apa pun, dan setiap document di dalamnya boleh membawa field yang berbeda. Sifat ini disebut flexible schema; sebutan lain yang sering muncul, schemaless, sebenarnya kurang tepat karena skema tetap ada — hanya berpindah tempat dari database ke masing-masing document.

Peta materi Chapter ini:

  • Perbedaan konsep schema-on-write dan schema-on-read.
  • Demo nyata: satu collection berisi empat bentuk document sekaligus.
  • Keuntungan dan risiko skema fleksibel bagi tim pengembang.
  • Kapan disiplin struktur tetap dibutuhkan.

Schema-on-Write versus Schema-on-Read #

Cara database menjaga bentuk data ada dua kutub. Database relasional menerapkan schema-on-write: setiap INSERT diperiksa terhadap struktur tabel pada saat penulisan, dan data yang menyimpang langsung ditolak. MongoDB tanpa validator bekerja dengan schema-on-read: server menerima document apa pun, dan struktur baru “diterapkan” ketika data dibaca atau diolah oleh kode aplikasi.

AspekRelasional (schema-on-write)MongoDB tanpa validator (schema-on-read)
Definisi strukturSaat membuat tabel (DDL)Tidak ada; tiap document membawa strukturnya sendiri
Data menyimpangDitolak dengan errorDiterima apa adanya
Menambah field baruALTER TABLE + migrasiLangsung tulis field pada document baru
Penjaga bentuk dataDatabaseKode aplikasi dan disiplin tim

Kedua kutub ini bukan kubu benar-salah, melainkan pilihan tempat logika diletakkan. Menaruh logika di database berarti deviasi terdeteksi sedini mungkin; menaruhnya di aplikasi berarti perubahan bentuk data bisa berjalan seiring cepatnya iterasi produk.

Satu Collection, Empat Bentuk #

Kita buktikan fleksibilitas itu pada collection produk di database latihan_validasi. Empat document berikut sengaja ditulis dengan bentuk berbeda — lengkap, minimal, salah ketik, dan bertipe teks:

js
use latihan_validasi
db.produk.drop()
db.produk.insertMany([
  { nama: "Kopi Gayo 250g", harga: 85000, kategori: "Minuman", stok: 40, sku: "MIN-001", tanggal_ditambahkan: new Date("2026-01-10") },
  { nama: "Teh Melati 100g", harga: 12000 },
  { nama: "Gula Pasir 1kg", hrga: 15000 },
  { nama: "Susu UHT 1L", harga: "delapan belas ribu" }
])
db.produk.find()

Output yang Dihasilkan:

text
switched to db latihan_validasi
true
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad38e76cefcefe48561ea5c'),
    '1': ObjectId('6ad38e76cefcefe48561ea5d'),
    '2': ObjectId('6ad38e76cefcefe48561ea5e'),
    '3': ObjectId('6ad38e76cefcefe48561ea5f')
  }
}
[
  {
    _id: ObjectId('6ad38e76cefcefe48561ea5c'),
    nama: 'Kopi Gayo 250g',
    harga: 85000,
    kategori: 'Minuman',
    stok: 40,
    sku: 'MIN-001',
    tanggal_ditambahkan: ISODate('2026-01-10T00:00:00.000Z')
  },
  {
    _id: ObjectId('6ad38e76cefcefe48561ea5d'),
    nama: 'Teh Melati 100g',
    harga: 12000
  },
  {
    _id: ObjectId('6ad38e76cefcefe48561ea5e'),
    nama: 'Gula Pasir 1kg',
    hrga: 15000
  },
  {
    _id: ObjectId('6ad38e76cefcefe48561ea5f'),
    nama: 'Susu UHT 1L',
    harga: 'delapan belas ribu'
  }
]
  1. Kopi Gayo — bentuk lengkap: nama, harga, kategori, stok, sku, dan tanggal_ditambahkan.
  2. Teh Melati — bentuk minimal: hanya nama dan harga; field lain belum diisi.
  3. Gula Pasir — salah ketik field: hrga, bukan harga.
  4. Susu UHT — tipe campur: harga berisi teks, bukan angka.

Setelah insertMany, kita bisa memeriksa tipe harga pada tiap document langsung dari server:

js
db.produk.aggregate([
  { $project: { _id: 0, nama: 1, tipe_harga: { $type: "$harga" } } }
])

Output yang Dihasilkan:

text
[
  { nama: 'Kopi Gayo 250g', tipe_harga: 'int' },
  { nama: 'Teh Melati 100g', tipe_harga: 'int' },
  { nama: 'Gula Pasir 1kg', tipe_harga: 'missing' },
  { nama: 'Susu UHT 1L', tipe_harga: 'string' }
]

Server melaporkan empat keadaan berbeda dari satu field yang secara bisnis bermakna sama: int, int, missing, dan string. Perhatikan bahwa angka literal 85000 di mongosh tersimpan sebagai int — perilaku angka literal yang kita bedah di Chapter BSON Types.

Iterasi Cepat dan Harganya #

Skema fleksibel memberi kecepatan pada tahap awal produk:

  • Tanpa migrasi saat menambah field — versi baru aplikasi cukup menulis rating atau diskon pada document baru; document lama tidak perlu disentuh.
  • Bentuk boleh berbeda sesuai konteks — produk fisik bisa punya dimensi, produk digital punya tautan_unduh, keduanya tetap satu collection.
  • Prototype bergerak secepat ide — struktur bisa diubah tanpa membuka sesi ALTER TABLE.

Kecepatan itu dibayar dengan risiko yang tidak terlihat sehari-hari:

  • Salah ketik field (hrga) membuat data “hilang” dari query harga tanpa error apa pun.
  • Tipe campur (harga sebagai teks) lolos masuk dan merusak perhitungan agregasi.
  • Field wajib bisa absen — document seperti Teh Melati sah-sah saja disimpan.

Kita uji ketiga risiko itu dalam satu query sederhana — tampilkan semua produk di bawah Rp50.000:

js
db.produk.find({ harga: { $lt: 50000 } })

Output yang Dihasilkan:

text
[
  {
    _id: ObjectId('6ad38e76cefcefe48561ea5d'),
    nama: 'Teh Melati 100g',
    harga: 12000
  }
]

Hanya Teh Melati yang muncul. Kopi Gayo memang di atas batas, tetapi dua sisanya hilang dengan alasan berbeda: Gula Pasir tidak memiliki field harga (yang ada hrga), sedangkan harga Susu UHT bertipe teks — dan dalam urutan tipe BSON, angka selalu lebih kecil daripada string sehingga perbandingan $lt tidak pernah cocok.

Kapan Disiplin Tetap Dibutuhkan #

Fleksibilitas paling aman dipakai ketika satu tim kecil menulis data dengan disiplin tinggi dan bentuk document masih sering berubah. Begitu aplikasi tumbuh — banyak layanan menulis ke collection yang sama, tim membesar, laporan keuangan bergantung pada konsistensi — kontrak bentuk data menjadi kebutuhan.

MongoDB menyediakan mekanisme untuk itu tanpa meninggalkan model document: schema validation, yaitu validator yang menempel pada collection dan memeriksa setiap insert serta update. Validator inilah tokoh utama sepanjang Section ini, mulai dari cara memasangnya di Chapter berikutnya. Sebelum itu, bersihkan database latihan:

js
db.dropDatabase()

Output yang Dihasilkan:

text
{ ok: 1, dropped: 'latihan_validasi' }

Rangkuman #

  • MongoDB menerapkan schema-on-read secara default: server menerima document bentuk apa pun, struktur dijaga oleh aplikasi.
  • Satu collection boleh berisi bentuk bercampur — lengkap, minimal, salah ketik, hingga tipe yang tidak sesuai.
  • Keuntungannya iterasi cepat tanpa migrasi; risikonya data tidak konsisten yang hilang diam-diam dari query.
  • Saat konsistensi menjadi kebutuhan, pasang schema validation pada collection — materi Chapter berikutnya.