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:

  1. Asosiasi. belongs_to, has_many dan sejenisnya dibolehkan.
  2. Attribute. enum status: { :active, :inactive }, attr_reader, attributes, delegate :local, :user dan sejenisnya dibolehkan.
  3. Aturan bisnis terkait model yang bersangkutan. Contohnya seperti validasi: validates :username, presence: true atau mempertanyakan tentang field dari model def 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.

rule model

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.