Memahami bahwa flexible schema bukan berarti tanpa desain, menguasai prinsip data locality dan kebiasaan menulis skema berorientasi query, serta mengenali anti-pattern seperti massive arrays dan excessive nesting beserta solusinya.

Sampai episode 5, kalian sudah mahir menulis query dalam berbagai bentuk. Sekarang tiba waktunya pertanyaan yang lebih mendasar: bagaimana seharusnya data disusun? Ini topik yang paling sering diabaikan — banyak developer merasa "schemaless" berarti bebas menaruh data sesuka hati. Faktanya, justru karena tidak ada schema kaku, kalian harus lebih disiplin dalam merancangnya.
Episode 6 menanamkan cara berpikir desainer schema MongoDB. Roadmap-nya: pertama kita luruskan mitos "schemaless = tanpa desain", kedua kita pahami prinsip emas data locality, ketiga kita pelajari bahwa schema harus mengikuti access pattern, dan keempat kita bedah anti-pattern paling umum beserta solusinya. Akhir episode, kalian akan melihat schema MongoDB sebagai keputusan arsitektur, bukan sekadar tempat menumpuk JSON.
Ada kesalahpahaman berbahaya: karena MongoDB tidak memaksa schema, maka desain tidak penting. Ini keliru total. Flexible schema justru memindahkan tanggung jawab dari database ke kalian sebagai arsitek data. Database tidak akan memprotes kalau kalian menyimpan email sebagai array alih-alih string — tapi aplikasi kalian akan runtuh saat dipakai memproduksi.
Bayangkan perbedaan ini: di RDBMS, schema adalah batasan yang melindungi kalian dari data kacau; di MongoDB, schema adalah keputusan yang harus kalian buat dengan kesadaran penuh. Pertanyaan kunci yang harus kalian jawab sebelum menulis dokumen pertama:
Jika kalian bisa menjawab tiga pertanyaan itu, kalian sudah punya dasar desain yang jauh lebih baik daripada kebanyakan developer yang asal menulis.
Salah satu cara menyalurkan disiplin desain adalah mendokumentasikan schema. Bisa lewat file JSON Schema untuk validasi (kita bahas di episode 8), bisa lewat library seperti Zod atau Mongoose di sisi aplikasi. Kuncinya: konsistensi struktur field — penamaan, tipe data, dan unit — harus disepakati lintas seluruh tim, atau aplikasi akan kacau saat dua service membaca field yang sama dengan asumsi berbeda.
Inilah prinsip nomor satu desain schema MongoDB, sering disebut data locality. Di RDBMS, kalian menormalkan data lalu menggabungkannya dengan JOIN saat membaca. Di MongoDB, penggabungan itu mahal — lebih baik meletakkan data yang sering dibaca bersamaan di dokumen yang sama.
Contoh paling jelas: halaman profil user di aplikasi sosial menampilkan nama, email, bio, dan jumlah follower secara bersamaan. Di RDBMS, itu bisa berarti menggabungkan tiga tabel. Di MongoDB, simpan semuanya dalam satu dokumen:
{
"_id": ObjectId("66xxxxxxxxxxxxxxxxxxxxxxxx"),
"username": "arman",
"email": "arman@example.com",
"bio": "Cloud engineer dan pembelajar seumur hidup",
"followerCount": 1280,
"profile": {
"location": "Jakarta",
"website": "https://arman.dev"
}
}Satu query find mengambil seluruh data yang dibutuhkan untuk halaman profil. Tidak ada JOIN, tidak ada query kedua. Inilah keunggulan performa utama document database.
Data locality biasanya berarti menduplikasi data di beberapa tempat. Misalnya nama user di-duplikasi ke setiap dokumen order yang pernah dibuatnya. Duplikasi ini disengaja — ia menukar konsistensi pembaruan (harus di-update di banyak tempat) dengan kecepatan baca. Kita akan membedah embedding vs referencing secara mendalam di episode 7; untuk sekarang, ingat prinsipnya: tempatkan data berdasarkan cara data itu dibaca, bukan berdasarkan teori normalisasi.
Schema MongoDB yang baik tidak dirancang dari sisi entitas ("user punya apa saja?"), melainkan dari sisi query ("query apa yang paling sering jalan?"). Mulailah dengan menuliskan daftar query yang akan paling sering dieksekusi aplikasi kalian, lalu rancang dokumen agar setiap query utama bisa diselesaikan dengan membaca sesedikit mungkin dokumen.
Langkah praktisnya:
db.orders.find({ userId: ObjectId("66xxxxxxxxxxxxxxxxxxxxxxxx") })Query di atas sangat umum: "tampilkan semua order user ini". Jika userId tidak ber-index, setiap eksekusi menjadi full scan. Memahami access pattern seperti ini akan memandu kalian menempatkan field dan memilih index — topik yang kita lanjutkan di episode 12.
Anti-pattern pertama: array yang tumbuh tanpa batas. Contoh paling umum: menyimpan semua komentar produk di dalam dokumen produk. Produk viral bisa mendapat puluhan ribu komentar, dan tiap operasi yang menyentuh dokumen produk harus memuat seluruh array itu — lambat dan boros memory.
{
"_id": ObjectId("66xxxxxxxxxxxxxxxxxxxxxxxx"),
"name": "Laptop Gaming",
"comments": [
{ "user": "budi", "text": "Keren!" },
{ "user": "siti", "text": "Harga oke" }
]
}Solusinya: pisahkan komentar ke collection terpisah dengan referensi ke produk. Array dalam dokumen sebaiknya dibatasi untuk relasi "one-to-few" — beberapa lusin elemen maksimal, bukan ribuan.
Anti-pattern kedua: embedding yang terlalu dalam dan lebar. Dokumen bersarang lebih dari tiga level menjadi sulit dibaca, sulit di-index, dan sulit di-query. Nested document yang kompleks membuat path query seperti profile.address.history.locations[...] terasa menyakitkan.
{
"user": {
"account": {
"settings": {
"notifications": {
"email": {
"marketing": true
}
}
}
}
}
}Solusinya: flat-kan struktur. Batasi kedalaman maksimal 3 level, dan untuk data yang benar-benar hierarkis (kategori, organisasi), pertimbangkan pola seperti array ancestors atau collection terpisah.
Anti-pattern ketiga: membawa kebiasaan RDBMS — memecah setiap entitas kecil-kecil ke collection terpisah lalu menyambungnya dengan $lookup di setiap query. Ini membuang seluruh keunggulan document database. Indikatornya: query kalian selalu diawali banyak $lookup, dan setiap pembacaan data butuh puluhan dokumen digabung.
Solusinya bukan normalisasi penuh atau denormalisasi penuh, tapi pendekatan hybrid yang kita bahas di episode 7: embed data yang dibaca bersama, referensikan data yang besar atau sering berubah sendiri.
Info
Ada kerangka berpikir cepat untuk memutuskan: tanyakan "apakah data ini selalu dibaca bersama dengan dokumen induk?" Jika ya dan jumlahnya sedikit, embed. Jika tidak, atau jumlahnya besar, referensikan. Keputusan ini jarang hitam-putih — itulah mengapa MongoDB memberi kalian kebebasan memilih, dan mengapa kalian harus memilih dengan sadar.
Pada episode 6 ini kalian telah memahami bahwa flexible schema justru menuntut desain yang lebih disiplin, menguasai prinsip utama data locality — data yang diakses bersama disimpan bersama — dan belajar merancang schema berorientasi access pattern, yaitu menyusun dokumen berdasarkan query yang paling sering dieksekusi. Kalian juga mengenali tiga anti-pattern utama: massive arrays yang tumbuh tak terkendali, excessive nesting melebihi tiga level, dan over-normalized schema yang membawa kebiasaan RDBMS ke MongoDB.
Inti yang harus dibawa pulang:
Di episode 7 berikutnya kita membedah salah satu keputusan terpenting dalam desain: Data Modeling: Embedding vs Referencing. Kalian akan mempelajari kapan meng-embed dokumen, kapan mereferensikan dengan ObjectId, pola one-to-few, one-to-many, dan many-to-many, serta pendekatan hybrid yang menggabungkan keduanya. Sampai jumpa di episode 7!