Tips saat bekerja dengan Active Record
Jika anda mengingat kembali hari-hari awal kita menulis kode Active Record mungkin sangat menyenangkan.
Namun seiringnya waktu, kode yang ditulis oleh active record menjadi sangat besar dan sulit dipelihara. Saya sudah pernah mengulas tentang hal ini ditulisan sebelumnya, mungkin anda bisa cek disini.
Pada tulisan ini saya ingin fokus memberikan beberapa tips yang mungkin dapat membuat kode anda lebih mudah dipelihara khusunya pada model anda.
Pertama: Jangan gunakan Active::Record callbacks seperti before_create
, after_create
dan sejenisnya.
Untuk list lengkap dari action-actionnya ada di dokumentasi ini.
Callback adalah kode proses bisnis yang sangat cepat untuk berubah-ubah(dinamis). Bagi saya model yang baik itu adalah model yang jarang untuk berubah. Selain itu, kode bisnis juga sangat bisa menjadi kompleks.
Contohnya ketika sebuah data dihapus, sistem bisa saja melakukan banyak sekali hal-hal yang berakhir dengan seratus baris untuk sebuah callback after_destroy
saja, belum termasuk before_destroy
atau after_create
jika mungkin nanti ada.
Alternativenya anda bisa menggunakan konsep Mutator.
Saya sudah pernah bahas konsep ini di tulisan yang sebelumnya.
Kedua: Model tidak diperbolehkan untuk memiki dependency.
Singkatnya, anda hanya boleh memangil method-method yang ada di dalam kelas yang bersangkutan saja dan tidak boleh memanggil kelas lain.
Contohnya seperti ini:
class User < ApplicationRecord
def add_log
Log.add_user_log("User log created")
end
end
Pada kode diatas anda memangil kelas Log
pada kelas User
, artinya kelas User
memiliki dependency pada kelas Log
. Artinya ketika model Log
anda ubah, maka model User
juga akan ikut berubah.
Rails menganut pola MVC pada developmentnya, dimana pada pola itu model adalah core kelas atau objek yang bisa dipanggil dibanyak tempat seperti controller, view atau service dan sebagainya.
Maka saya tidak ingin model memiliki dependency pada model lain atau kelas lain.
Jika pada suatu model terdapat dependency maka rantaian dependency pada sebuah proses tersebut akan menjadi sangat besar sehingga kode sulit untuk dipelihara.
Hal ini bisa terealisasi jika anda tidak menggunakan callback
, tips yang sebelumnya.
Ketiga: Perlunya sebuah standard
Berdasarkan tips-tips sebelumnya, maka anda butuh sebuah standard atau rule dalam menulis kode model.
Active Record sangat-sangat ideal sekali dalam menampilkan data, namun cukup buruk untuk menulis atau memberharui data pada database.
Maka anda perlu rule yang cukup ketat untuk membatasi domain pada model ini, rule ini saya ambil dari sebuah buku yang ditulis oleh Ivan Nemytchenko.
Model hanya dibolehkan memiliki 3 hal ini:
- Asosiasi.
belongs_to
,has_many
dan sejenisnya dibolehkan. - Attribute.
enum status: { :active, :inactive }
,attr_reader
,attributes
,delegate :local, :user
dan sejenisnya dibolehkan. - Aturan bisnis terkait model yang bersangkutan. Contohnya seperti validasi:
validates :username, presence: true
atau mempertanyakan tentang field dari modeldef admin?; end
Model tidak boleh memiliki:
-
Aplikasi logic seperti:
class User has_many :items def add_item(item) items.create(item.attributes) end
- Jangan melakukan otorisasi seperti
User.can_edit_post(post)
, lebih baik gunakan policy object. -
Penggunaan
scope
sebaiknya juga dihindari, lebih baik gunakan repository pattern, contohnya seperti kode ini:# app/repositories/user_repository.rb module UserRepository extend ActiveSupport::Concern included do scope :admins, -> { where(admin: true) } end end # app/model/user.rb class User < ApplicationRecord include UserRepository end # anda bisa memanggilnya seperti ini User.admins
Maka, saya melihat sebuah objek model itu lebih mempresentasikan sebuah record dibandingkan sebuah table.
Keempat: Gunakan Static Analyzer
Salah satunya adalah Rubocop anda bisa set di file rubocop.yml
Rails:
Enabled: true
Dengan static analyzer anda bisa mengunakan method-method yang sesuai standard yang direkomendasikan oleh komunitas Rails.
Kelima: Gunakan fitur namespacing
Jangan menaruh sebuah model pada level yang sama, misalnya
/models
-- user.rb
-- article.rb
-- article_comment.rb
Tapi coba berikan namespace
pada model anda sehingga arsitektur folder anda bisa menceritakan sistem aplikasi apa yang anda buat. Anda bisa lakukan refactor menjadi seperti ini.
/models
-- user.rb
-- article.rb
/article
- comment.rb
Artinya model comment anda simpan di dalam modul article:
module Article
class Comment < ApplicationRecord
end
end
Keenam: Gunakan annotate_models
Annotate_models adalah sebuah gem yang memberikan komentar daftar field-field apa saja yang ada pada sebuah model. Anda bisa cek dokumentasinya disini.
Terakhir: Jangan lupa untuk uji model anda.
Sebenernya tips ini lebih penting dari semuanya, namun saya taruh di paling terakhir karena saya anggap anda sudah paham pentingnya kode test ini.
Jika anda ingin mengimplementasikan tips-tips diatas pada kode legacy sebaiknya gunakan metode cover and modify yaitu tulis kode testnya dulu baru kemudian ubah kode produksinya.
Kesimpulan
Mari kita buatkan list dari tips-tips sebelumnya dibagian ini:
- Jangan gunakan Active::Record callbacks seperti
before_create
,after_create
dan sejenisnya. - Model tidak diperbolehkan untuk memiki dependency
- Perlunya Sebuah Standard
- Gunakan Static Analyzer
- Gunakan fitur namespacing (module)
- Gunakan annotate_models
- Jangan lupa untuk uji model anda.
Tips ini tidak bersifat mutlak, mungkin ada beberapa yang belum tercover. Jika nanti saya menemukan hal yang baru, saya akan update lagi.
Jika anda memiliki preference atau opini yang berbeda dengan saya, anda bisa berikan kritik dan saran kepada saya. Sekian saja untuk tulisan ini, terima kasih.