Saya telah mengerjakan aplikasi Ruby on Rails besar selama beberapa tahun. Itu diwarisi dalam kondisi yang buruk, tetapi sebagian besar bug produksi telah diperbaiki dengan waktu. Ada beberapa bagian yang belum tersentuh seperti kode pemrosesan pembayaran. Kode ini berfungsi sebagian besar, kecuali bahwa setiap kali tagihan ditolak oleh prosesor pembayaran, pengguna mendapat 500 kesalahan alih-alih pesan yang bermanfaat. Saya ingin memperbaiki kode agar lebih mudah dipelihara. Saya akan memberikan penjelasan singkat tentang cara kerjanya.
Saya telah menghapus semua kode penanganan kesalahan dari cuplikan berikut.
Labirin dimulai dalam sebuah pengontrol:
def submit_credit_card
...
@credit_card = CreditCard.new(params[:credit_card].merge(:user => @user))
@credit_card.save
...
@submission.do_initial_charge(@user)
...
end
Kemudian dalam Submission
model:
def do_initial_charge(user)
...
self.initial_charge = self.charges.create(:charge_type => ChargeType.find(1), :user => user)
self.initial_charge.process!
self.initial_charge.settled?
end
Dalam Charge
model:
aasm column: 'state' do
...
event :process do
transitions :from => [:created, :failed], :to => :settled, :guard => :transaction_successful?
end
...
end
def initialize(*params)
super(*params)
...
self.amount = self.charge_type.amount
end
def transaction_successful?
user.reload
credit_card = CreditCard.where(user_id: user_id).last
cct = self.cc_transactions.build(:user => user, :credit_card => credit_card, :cc_last_four => credit_card.num_last_four, :amount => amount, :charge_id => id)
cct.process!
if self.last_cc_transaction.success
self.update_attribute(:processed, Time.now)
return true
else
self.fail!
return false
end
end
Ada banyak bit yang dipertanyakan di atas seperti memuat ulang user
dan menemukan yang terakhir CreditCard
daripada melewati yang baru saja disimpan. Juga kode ini tergantung pada ChargeType
dimuat dari database dengan ID yang dikodekan keras.
Di CcTransaction
kami melanjutkan jejak:
def do_process
response = credit_card.process_transaction(self)
self.authorization = response.authorization
self.avs_result = response.avs_result[:message]
self.cvv_result = response.cvv_result[:message]
self.message = response.message
self.params = response.params.inspect
self.fraud_review = response.fraud_review?
self.success = response.success?
self.test = response.test
self.response = response.inspect
self.save!
self.success
end
Semua ini tampaknya dilakukan adalah menyimpan catatan di cc_transactions
tabel database. Pemrosesan pembayaran aktual dilakukan dalam CreditCard
model. Saya tidak akan membuat Anda bosan dengan detail kelas itu. Pekerjaan yang sebenarnya dilakukan oleh ActiveMerchant::Billing::AuthorizeNetCimGateway
.
Jadi kita memiliki setidaknya 5 model yang terlibat ( Submission
, Charge
, ChargeType
, CcTransaction
, dan CreditCard
). Jika saya melakukan ini dari awal, saya hanya akan menggunakan Payment
model tunggal . Hanya ada 2 jenis biaya, jadi saya akan mengkodekan nilai-nilai tersebut sebagai variabel kelas. Kami tidak menyimpan detail kartu kredit, sehingga model itu tidak perlu. Info transaksi dapat disimpan dalam payments
tabel. Pembayaran gagal tidak perlu disimpan.
Saya bisa masuk dan melakukan refactoring ini dengan cukup mudah kecuali untuk persyaratan bahwa tidak ada yang salah pada server produksi. Setiap kelas redundan memiliki banyak metode yang dapat dipanggil dari mana saja di basis kode. Ada serangkaian tes integrasi tetapi cakupannya tidak 100%.
Bagaimana saya bisa memperbaiki ini sambil memastikan tidak ada yang rusak? Jika saya melewati 5 kelas pembayaran dan grep
mengedit setiap metode untuk mencari tahu di mana mereka dipanggil ada kemungkinan besar saya akan melewatkan sesuatu. Klien sudah terbiasa dengan bagaimana kode saat ini berjalan dan memperkenalkan bug baru tidak dapat diterima. Selain meningkatkan cakupan tes menjadi 100%, apakah ada cara untuk memperbaiki ini dengan pasti bahwa tidak ada yang akan rusak?
AASM::InvalidTransition: Event 'process' cannot transition from 'failed'
pengecualian yang menutupi kesalahan nyata yang merupakan transaksi yang gagal. Ada banyak tipuan sehingga sulit untuk mendapatkan respons kembali ke pengguna dan memungkinkan pengiriman kembali. Saya yakin itu mungkin tetapi tampaknya hampir sesulit refactoring.