Avoid ORM

Avoid ORM #

ORM (Object-Relational Mapping) sering dianggap sebagai silver bullet dalam pengembangan aplikasi backend. Ia menjanjikan produktivitas tinggi, kode yang rapi, dan abstraksi database yang nyaman. Namun di aplikasi skala menengah hingga besar, penggunaan ORM yang tidak terkontrol justru sering menjadi sumber utama masalah performa, beban database yang tidak perlu, dan technical debt jangka panjang.

Artikel ini membahas mengapa penggunaan ORM perlu dibatasi, dampaknya terhadap database jika digunakan secara sembarangan, serta best practice penggunaan ORM atau alternatifnya agar aplikasi tetap optimal dan scalable.

Apa Itu ORM (Secara Singkat) #

ORM adalah lapisan abstraksi yang memetakan:

  • Table → Object / Struct
  • Row → Instance
  • Query SQL → Method / Function

Contoh ORM populer:

  • Hibernate / JPA (Java)
  • ActiveRecord (Rails)
  • Sequelize / TypeORM (Node.js)
  • GORM (Golang)

Masalahnya bukan pada ORM itu sendiri, melainkan bagaimana ORM digunakan.


Masalah Utama ORM dalam Aplikasi yang Mengejar Performa #

Query Tidak Terlihat (Hidden Query) #

ORM menyembunyikan SQL di balik method yang terlihat sederhana:

user.Preload("Orders").Preload("Items").Find(&users)

Di balik layar, ini bisa menghasilkan:

  • 1 query utama
  • N query relasi
  • Query tambahan karena lazy loading

Developer sering tidak sadar berapa query yang benar-benar dieksekusi.

Dampak ke database:

  • Lonjakan query kecil (chatty query)
  • Query burst tanpa disadari
  • Beban CPU dan connection pool meningkat

N+1 Query Problem (Masalah Klasik) #

Kasus umum:

  1. Ambil 100 user
  2. Untuk setiap user, ORM melakukan query ke tabel relasi

Hasilnya:

  • 1 query user
  • 100 query tambahan relasi

Dampak ke database:

  • Query count melonjak drastis
  • Latency meningkat
  • DB terlihat “sibuk” padahal datanya kecil

Masalah ini tidak mungkin terjadi jika developer menulis SQL eksplisit dengan JOIN yang benar.

Overfetching Data (Ambil Data Berlebihan) #

ORM sering mendorong pola:

db.Find(&users)

Yang artinya:

  • SELECT * FROM users

Padahal kebutuhan API sering kali hanya:

  • id
  • name
  • status

Dampak ke database dan network:

  • Ukuran result set membengkak
  • Network I/O meningkat
  • Memory usage di DB dan aplikasi naik

Ini sangat berbahaya di:

  • Table besar
  • Kolom text / json / blob

Sulit Mengontrol Index Usage #

ORM cenderung:

  • Menghasilkan query generik
  • Menambahkan kondisi otomatis
  • Menggunakan fungsi atau casting implisit

Contoh:

  • WHERE DATE(created_at) = ?
  • WHERE LOWER(email) = ?

Dampak ke database:

  • Index tidak terpakai
  • Full table scan
  • Query planner bekerja lebih berat

Padahal SQL manual bisa ditulis jauh lebih index-friendly.

Query Kompleks Menjadi Tidak Natural #

Ketika bisnis logic berkembang:

  • Aggregation
  • Window function
  • Subquery
  • CTE

ORM mulai terasa:

  • Dipaksakan
  • Tidak readable
  • Lebih rumit dari SQL aslinya

Akhirnya:

  • Developer tetap menulis raw SQL
  • Tapi ORM masih dipakai setengah-setengah
  • Arsitektur menjadi tidak konsisten

Dampak Jangka Panjang ke Database #

Jika ORM digunakan secara sembarangan:

  1. Database menjadi bottleneck utama
  2. Scaling database lebih cepat dan mahal
  3. Sulit melakukan query tuning
  4. EXPLAIN plan jarang dianalisis
  5. Masalah performa sulit direproduksi

Banyak kasus “database tiba-tiba lambat” ternyata bukan karena DB-nya, tetapi karena:

ORM menghasilkan query yang buruk dalam volume besar.


Kapan ORM Masih Masuk Akal Digunakan? #

ORM bukan musuh, tapi harus ditempatkan dengan tepat.

ORM masih masuk akal untuk:

  • CRUD sederhana
  • Table kecil
  • Internal tools
  • Admin panel
  • Prototype / MVP
  • Operasi non-critical

Dengan syarat:

  • Query diaudit
  • Logging query aktif
  • N+1 problem dimonitor

Best Practice Menggunakan ORM (atau Menghindarinya) #

Gunakan ORM Hanya di Layer Tertentu #

Pisahkan:

  • Service layer → bisnis logic
  • Repository layer → data access

Di repository:

  • ORM untuk query sederhana
  • Raw SQL untuk query berat

Prefer Explicit Query daripada Magic ORM #

Contoh (Golang):

  • ORM untuk insert/update sederhana
  • Raw query untuk list, report, analytics

Ini membuat:

  • Query jelas
  • Mudah di-EXPLAIN
  • Mudah dioptimasi

Gunakan DTO / Projection #

Jangan map hasil query langsung ke entity besar.

Gunakan struct khusus:

  • Response API
  • Read model

Hasilnya:

  • Data lebih kecil
  • Query lebih terkontrol
  • Entity tidak tercemar kebutuhan API

Aktifkan Query Logging dan Monitoring #

Wajib:

  • Log semua query lambat
  • Monitor query count per request
  • Alarm jika query tiba-tiba melonjak

Tanpa ini, ORM akan “diam-diam” menyabotase performa.

Jangan Takut Menulis SQL #

SQL adalah:

  • Bahasa native database
  • Paling optimal
  • Paling jujur soal performa

Developer backend wajib bisa SQL, bukan hanya ORM syntax.

ORM seharusnya membantu, bukan menggantikan pemahaman database.


Pendekatan Alternatif yang Lebih Scalable #

Banyak tim besar memilih:

  • Hybrid approach

    • ORM untuk write sederhana
    • SQL manual untuk read-heavy
  • Query Builder ringan

    • Lebih eksplisit dari ORM
    • Lebih aman dari raw string
  • CQRS ringan

    • Write model ≠ Read model

Pendekatan ini terbukti:

  • Lebih predictable
  • Lebih mudah di-scale
  • Lebih ramah database

Penutup #

ORM bukan masalah — penggunaan ORM tanpa kontrol adalah masalah.

Jika aplikasi Anda:

  • Mulai lambat tanpa sebab jelas
  • Database cepat penuh beban
  • Query sulit dianalisis

Maka sangat besar kemungkinan:

ORM digunakan terlalu jauh dari batas idealnya.

Gunakan ORM dengan sadar, pahami SQL di baliknya, dan jangan ragu turun ke level database ketika performa menjadi prioritas.

Karena pada akhirnya:

Aplikasi cepat lahir dari query yang jujur, bukan abstraksi yang terlalu nyaman.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact