Setiap sistem yang menyimpan data nyata dimulai dari pertanyaan: entitas apa saja yang terlibat, dan bagaimana hubungannya? Pada Bagian 1 kita merancang domain model perpustakaan sebagai data class Kotlin. Model ini menjadi fondasi untuk operasi CRUD dan transaksi peminjaman di bagian berikutnya.
Mengapa Domain Modeling Dulu #
Domain modeling adalah proses menangkap entitas dan aturan bisnis ke dalam tipe data. Tanpa model yang jelas, operasi CRUD akan menyebar di banyak tempat dan logika bisnis akan rusak. Dengan model yang baik, compiler membantu menangkap kesalahan tipe sebelum program dijalankan.
Tiga entitas inti perpustakaan kita:
Buku— katalog item yang dapat dipinjam,Anggota— pendaftar yang berhak meminjam,Transaksi— catatan peminjaman yang menghubungkan buku dan anggota.
Data Class untuk Entitas #
Kotlin menyediakan data class yang otomatis menghasilkan equals, hashCode, toString, dan copy. Empat method ini sering dibutuhkan pada entitas domain, dan menulisnya manual di Java memakan puluhan baris per class:
// Bagian 1: Pemodelan domain.
// Mendemonstrasikan data class Buku, Anggota, Transaksi beserta relasi
// dan enum status. Domain modeling adalah langkah pertama sebelum operasi
// CRUD dan transaksi peminjaman dibangun.
data class Buku(
val id: String,
val judul: String,
val penulis: String,
val isbn: String,
val tahunTerbit: Int,
val stok: Int = 1
)
data class Anggota(
val id: String,
val nama: String,
val email: String,
val aktif: Boolean = true
)
enum class StatusTransaksi { AKTIF, DIKEMBALIKAN, TERLAMBAT }
data class Transaksi(
val id: String,
val bukuId: String,
val anggotaId: String,
val status: StatusTransaksi = StatusTransaksi.AKTIF
)
fun main() {
val buku1 = Buku("B001", "Belajar Kotlin", "Budi Santoso", "978-602-001", 2026, stok = 2)
val buku2 = Buku("B002", "Pemrograman Modern", "Siti Aminah", "978-602-002", 2025, stok = 1)
val anggota = Anggota("A001", "Dewi Lestari", "[email protected]")
println("=== Buku ===")
listOf(buku1, buku2).forEach { b ->
println("[${b.id}] ${b.judul} oleh ${b.penulis} (${b.tahunTerbit}) stok=${b.stok}")
}
println()
println("=== Anggota ===")
println("[${anggota.id}] ${anggota.nama} <${anggota.email}> aktif=${anggota.aktif}")
println()
println("=== Transaksi ===")
val t1 = Transaksi("T001", buku1.id, anggota.id)
println("[${t1.id}] buku=${t1.bukuId} anggota=${t1.anggotaId} status=${t1.status}")
val t2 = t1.copy(id = "T002", status = StatusTransaksi.DIKEMBALIKAN)
println("[${t2.id}] buku=${t2.bukuId} anggota=${t2.anggotaId} status=${t2.status}")
println()
println("Jumlah total entitas: buku=2 anggota=1 transaksi=2")
}Output yang Dihasilkan:
=== Buku ===
[B001] Belajar Kotlin oleh Budi Santoso (2026) stok=2
[B002] Pemrograman Modern oleh Siti Aminah (2025) stok=1
=== Anggota ===
[A001] Dewi Lestari <[email protected]> aktif=true
=== Transaksi ===
[T001] buku=B001 anggota=A001 status=AKTIF
[T002] buku=B001 anggota=A001 status=DIKEMBALIKAN
Jumlah total entitas: buku=2 anggota=1 transaksi=2Mari kita bedah elemen penting:
data class Buku(...): keyworddatamemicu compiler membuatequalsberdasarkan seluruh property. Dua buku dengan ID sama tetap dianggap berbeda jika field lain berbeda.val stok: Int = 1: nilai default. Saat membuat buku baru tanpa menyebutstok, nilai1dipakai.val aktif: Boolean = true: status default anggota baru adalah aktif. Anggota yang dinonaktifkan tidak dapat meminjam, tetapi tetap tercatat.enum class StatusTransaksi { AKTIF, DIKEMBALIKAN, TERLAMBAT }: tipe terbatas dengan tiga nilai.whenatas enum bersifat exhaustif; lupa menangani satu nilai akan menghasilkan peringatan compiler.val t2 = t1.copy(id = "T002", status = StatusTransaksi.DIKEMBALIKAN):copy()membuat instance baru dengan satu atau dua field diubah. Instance aslit1tidak berubah (immutable).
Mengapa Transaksi Menyimpan ID, Bukan Objek #
Transaksi hanya menyimpan bukuId dan anggotaId, bukan Buku atau Anggota secara langsung. Tiga alasan:
- Serialisasi: JSON hanya menyimpan ID, bukan objek lengkap. Saat dimuat kembali, referensi di-resolve melalui repo.
- Konsistensi: jika data
Anggotadiperbarui, transaksi yang ada tidak perlu diubah. - Efisiensi memori: menyimpan ID ringan; menyimpan objek penuh menduplikasi data.
Pola ini disebut aggregation by reference dan umum di sistem informasi.
Enum dan State Mesin #
StatusTransaksi memiliki tiga nilai yang membentuk alur state mesin:
AKTIF— transaksi dibuat, buku sedang dipinjam.DIKEMBALIKAN— buku sudah dikembalikan, transaksi selesai.TERLAMBAT— peminjaman melewati batas waktu (akan dihitung di Bagian 3).
Perpindahan state yang valid: AKTIF -> DIKEMBALIKAN, AKTIF -> TERLAMBAT. Perpindahan lain seperti DIKEMBALIKAN -> AKTIF tidak valid dan harus ditolak.
Referensi Lanjutan #
- Kotlin documentation home
— halaman utama dokumentasi Kotlin untuk
data classdanvalue classpemodelan domain - Kotlin Collections overview — pengantar koleksi Kotlin sebagai struktur data anggota dan buku perpustakaan
Ringkasan Bagian 1 #
Domain model perpustakaan telah siap: tiga data class (Buku, Anggota, Transaksi) dan satu enum (StatusTransaksi). copy() mendukung pembuatan variasi tanpa mutasi. Pada Bagian 2 kita membangun operasi CRUD untuk menambah, mencari, memperbarui, dan menghapus entitas ini.