Tôi đã làm việc trên một ứng dụng Ruby on Rails lớn trong vài năm. Nó được thừa hưởng ở một trạng thái nghèo nàn nhưng hầu hết các lỗi sản xuất đã được giải quyết theo thời gian. Có một số phần chưa được chạm vào như mã xử lý thanh toán. Mã này hoạt động với hầu hết các phần, ngoại trừ bất cứ khi nào bộ xử lý thanh toán bị từ chối, người dùng sẽ nhận được một lỗi 500 thay vì một thông báo hữu ích. Tôi muốn cấu trúc lại mã để dễ bảo trì hơn. Tôi sẽ cung cấp sơ lược về cách thức hoạt động của nó.
Tôi đã xóa tất cả mã xử lý lỗi khỏi các đoạn mã sau.
Mê cung bắt đầu trong một bộ điều khiển:
def submit_credit_card
...
@credit_card = CreditCard.new(params[:credit_card].merge(:user => @user))
@credit_card.save
...
@submission.do_initial_charge(@user)
...
end
Sau đó, trong Submission
mô hình:
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
Trong Charge
mô hình:
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
Có rất nhiều bit đáng nghi ngờ ở trên như tải lại user
và tìm cái cuối cùng CreditCard
thay vì chuyển trong cái vừa lưu. Ngoài ra mã này phụ thuộc vào ChargeType
tải từ cơ sở dữ liệu với ID được mã hóa cứng.
Trong CcTransaction
chúng tôi tiếp tục xuống đường mòn:
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
Tất cả điều này dường như phải làm là lưu một bản ghi trong cc_transactions
bảng cơ sở dữ liệu. Việc xử lý thanh toán thực tế được thực hiện trong CreditCard
mô hình. Tôi sẽ không làm bạn nhàm chán với các chi tiết của lớp học đó. Các công việc thực tế được thực hiện bởi ActiveMerchant::Billing::AuthorizeNetCimGateway
.
Vì vậy, chúng ta có ít nhất 5 mô hình liên quan ( Submission
, Charge
, ChargeType
, CcTransaction
, và CreditCard
). Nếu tôi làm điều này từ đầu, tôi sẽ chỉ sử dụng một Payment
mô hình duy nhất . Chỉ có 2 loại phí, vì vậy tôi sẽ mã hóa các giá trị đó dưới dạng các biến lớp. Chúng tôi không lưu trữ chi tiết thẻ tín dụng, vì vậy mô hình đó là không cần thiết. Thông tin giao dịch có thể được lưu trữ trong payments
bảng. Thanh toán thất bại không cần phải được lưu.
Tôi có thể đi vào và thực hiện việc tái cấu trúc này khá dễ dàng ngoại trừ yêu cầu không có gì sai sót trên máy chủ sản xuất. Mỗi lớp dự phòng có nhiều phương thức có thể được gọi từ bất kỳ đâu trong cơ sở mã. Có một bộ các bài kiểm tra tích hợp nhưng phạm vi bảo hiểm không phải là 100%.
Làm thế nào tôi nên đi về tái cấu trúc điều này trong khi đảm bảo không có gì phá vỡ? Nếu tôi đã trải qua 5 lớp thanh toán và chỉnh sửa grep
mọi phương thức để tìm ra nơi chúng được gọi là có xác suất cao tôi sẽ bỏ lỡ điều gì đó. Máy khách đã quen với cách mã hiện tại chạy và giới thiệu bất kỳ lỗi mới nào là không thể chấp nhận được. Ngoài việc tăng phạm vi kiểm tra lên 100%, có cách nào để tái cấu trúc điều này một cách chắc chắn rằng không có gì sẽ phá vỡ?
AASM::InvalidTransition: Event 'process' cannot transition from 'failed'
ngoại lệ che giấu lỗi thực sự là một giao dịch không thành công. Có quá nhiều quyết định, thật khó để lấy lại phản hồi cho người dùng và cho phép gửi lại. Tôi chắc chắn là có thể nhưng dường như khó như tái cấu trúc.