Dari 21 Tabel ke Arsitektur Berlapis: Pelajaran Merancang Aplikasi POS Desktop
Bagaimana pola Model–DAO–Service–UI menyelamatkan aplikasi Java Swing dengan 21 tabel dari nasib menjadi spaghetti code.
AppKasir — aplikasi Point of Sale desktop yang saya bangun dengan Java Swing — punya 21 tabel database yang saling terelasi: data master, transaksi penjualan, pembelian, retur dua arah, sampai tabel penomoran otomatis. Artikel ini tentang keputusan yang membuat kode seukuran itu tetap bisa dipelihara.
Godaan terbesar Swing: logika di dalam form
Pola yang paling sering ditemui di aplikasi Swing: semua kode — query SQL, kalkulasi, validasi — ditulis langsung di event handler tombol.
// Anti-pattern: semuanya di dalam form
private void btnSimpanActionPerformed(ActionEvent evt) {
Connection conn = DriverManager.getConnection(URL, USER, PASS);
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO penjualan ...");
// ...50 baris query + kalkulasi + update stok di sini
}Untuk 3 form ini masih "jalan". Untuk 20+ form dengan alur transaksi yang saling memutasi stok, ini bencana: logika stok tersebar di banyak tempat dan setiap perubahan berisiko merusak alur lain.
Pemisahan tanggung jawab: Model–DAO–Service–UI
Solusinya klasik tapi efektif — layered architecture:
Form / JDialog → presentasi, event UI
TableModel → binding data ke JTable
Service → logika bisnis (interface + implementasi)
DAO → seluruh akses database
Model → entity murniAturan mainnya ketat: form tidak boleh menyentuh SQL, dan DAO tidak boleh berisi aturan bisnis. Contoh alur transaksi penjualan:
@Override
public void simpanTransaksi(Penjualan penjualan) {
// Aturan bisnis hidup di satu tempat
validasiStok(penjualan.getDetail());
String nomor = nomorDao.generateNomorTransaksi("PJ");
penjualan.setNomor(nomor);
penjualanDao.insert(penjualan);
for (PenjualanDetail d : penjualan.getDetail()) {
stokDao.kurangiStok(d.getProdukId(), d.getJumlah());
}
}Ketika kemudian modul retur penjualan dan retur pembelian ditambahkan, keduanya tinggal mengikuti pola yang sama — memanggil stokDao yang sudah teruji, bukan menulis ulang mutasi stok.
Skema dulu, kode kemudian
Pelajaran kedua: waktu yang dihabiskan mendesain 21 tabel dengan relasi yang benar terbayar berkali-kali lipat. Contohnya pemisahan header–detail untuk semua jenis transaksi:
CREATE TABLE penjualan (
id INT PRIMARY KEY AUTO_INCREMENT,
nomor VARCHAR(20) UNIQUE NOT NULL,
tanggal DATETIME NOT NULL,
karyawan_id INT NOT NULL REFERENCES karyawan(id)
);
CREATE TABLE penjualan_detail (
id INT PRIMARY KEY AUTO_INCREMENT,
penjualan_id INT NOT NULL REFERENCES penjualan(id),
produk_id INT NOT NULL REFERENCES produk(id),
jumlah INT NOT NULL,
harga DECIMAL(12,2) NOT NULL
);Karena struktur konsisten di semua transaksi (penjualan, pembelian, retur), modul pelaporan JasperReports hampir "gratis" — template laporan tinggal melakukan join pola yang sama.
Keamanan di aplikasi desktop tetap wajib
Meski berjalan offline, AppKasir tetap menerapkan RBAC Admin/Kasir dengan password ter-hash SHA-256 dan pembatasan menu per peran. Aplikasi desktop bukan alasan mengabaikan pemisahan wewenang — kasir tidak boleh bisa menghapus data master atau melihat laporan laba.
Kesimpulan
Tiga hal yang saya bawa dari proyek ini:
- Arsitektur menentukan biaya fitur berikutnya. Modul retur murah karena fondasi Model–DAO–Service sudah benar.
- Skema relasional yang baik adalah setengah aplikasi. Konsistensi struktur membuat pelaporan dan analitik mudah.
- Pola lama tetap relevan. Layered architecture bukan barang baru — tapi untuk aplikasi data-heavy, ia tetap salah satu alat paling efektif yang kita punya.