Dasar-Dasar Desain Skema
Skema adalah cetak biru datamu: tabel apa saja yang ada, kolom apa yang dimilikinya, dan bagaimana mereka saling berhubungan. Desain skema yang baik sebagian besar adalah akal sehat ditambah beberapa kebiasaan.
- Satu tabel per "hal." Users, posts, orders — masing-masing punya tabelnya sendiri.
- Setiap baris butuh ID unik (primary key).
- Hubungkan tabel dengan foreign key, bukan dengan menyalin data ke sana kemari.
- Pilih tipe yang jujur. Uang adalah
decimal, bukanfloat. Tanggal adalah timestamp, bukan teks. - Jangan duplikasi. Kalau email seorang pengguna tersimpan di lima tabel, cepat atau lambat kamu akan punya lima email yang berbeda-beda.
Berikut skema sederhana untuk sebuah blog, jenis yang akan dibuatkan AI untukmu:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_id UUID NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
body TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_posts_author ON posts(author_id);
Baris author_id ... REFERENCES users(id) itu adalah foreign key-nya. Ia menghubungkan setiap post kembali ke tepat satu pengguna yang benar-benar ada, sehingga basis data itu sendiri menolak menyimpan post tanpa penulis yang valid:
users posts
┌─────────────┐ ┌─────────────┐
│ id (PK) ◀──┼────────────────┤ author_id │ (FK → users.id)
│ email │ one user │ id (PK) │
│ created_at │ has many │ title │
└─────────────┘ posts │ body │
│ published │
1 ────────────────────▶ *└─────────────┘
(one) (many)
Perhatikan REFERENCES (foreign key yang mengikat sebuah post ke pengguna yang benar-benar ada) serta constraint NOT NULL dan UNIQUE. Ini adalah pagar pengaman yang ditegakkan basis data untukmu, sehingga data buruk tidak bisa menyelinap masuk bahkan ketika kodemu punya bug.
Satu constraint yang layak ditambahkan secara sengaja adalah apa yang terjadi saat induknya (parent) hilang. Secara default, menghapus pengguna yang post-nya masih merujuk padanya akan gagal — dan itu sering kali memang yang kamu inginkan. Tapi kamu bisa membuatnya eksplisit: REFERENCES users(id) ON DELETE CASCADE akan menghapus post-nya juga, sementara ON DELETE RESTRICT memblokir penghapusan tersebut. Memilih dengan sengaja lebih baik daripada menemukan perilaku default lewat cara yang menyakitkan di production. Insting "biarkan basis data melindungimu" yang sama juga berlaku untuk constraint CHECK (CHECK (price >= 0)) — ia menolak data yang tidak masuk akal langsung dari sumbernya, alih-alih mempercayai setiap jalur kode untuk memvalidasi.