+1

Tại sao nên dùng Service Object trong code của bạn?

1. Service Objects là gì

Như ta đã biết, Rails là một trong những adaptors sử dụng mô hình MVC (Model, View, Controller). Đối với dự án nhỏ, khi các chức năng chưa có nhiều, thì việc sử dụng mô hình này tương đối hiệu quả. Tuy nhiên, đối với các dự án lớn hơn khi các method có xu hướng đẩy vào các ActiveRecord models thì chúng ta sẽ thấy việc đọc hiểu dựa vào mô hình này tương đối phức tạp. Do đó, chúng ta phải chia tách các chức năng thành các Service Objects.

Hay nói cách khác Service Objects là đoạn code chứa các business logic, nhìn vào nó, người lập trình có thể biết là application có những chức năng như thế nào.

Ví dụ:

Khi chúng ta tạo chứng năng push notification cho clients sau khi tạo 1 record nào đó, Bạn sử dụng controller làm nơi viết code

 # app/controllers/test_controller.rb #create
   def create
   ...
   if @test.save
     # code push notification
   else
     # code
  end
  ...

Thời gian đầu, chức năng của bạn chạy ổn. Nhưng khi khách hàng thêm yêu cầu, tôi muốn push notification sau khi click 1 button nào đó. Okie, thật sự không ổn nếu viết đi viết lại đoạn code trên vào các controller với từng loại button. Bạn nghĩ đến việc ném nó vào model

# app/models/control_test_push_notification.rb
  class ControlTestPushNotification < ActiveRecord::Base
    # code

    # this method is called from the controller
    def push_notifications(record)
      # code
    end
  end

Code lại chạy ổn, và cứ mỗi lần có yêu cầu, bạn lại thêm 1 method thoả mãn yêu cầu => model trên sẽ phình to, dẫn đến việc khó test, khó maintain. Mỗi lần viết rspec là bạn phải tạo 1 đống dữ liệu liên quan để test

  • Và điều thứ 2 chúng ta cần nhận ra: send notifications không phải là method cốt lõi của ControlTestPushNotification. vì chức năng côt lỗi của ControlTestPushNotification là các method của ControlTestPushNotification Class

Để giải quyết triệt để vấn đề này chúng ta cần di chuyện các logic của push notification vào một ServiceObject

2. Xây dựng ServiceObject

  • Chúng ta sẽ tạo 1 file trong thư mục service của app
app/
  assets/
  controllers/
  helpers/
  mailers/
  models/
  services/
    push_notification_service.rb
  views/

Tiếp theo chúng ta tạo 1 PushNotificationService trong đó

# app/services/push_notification_service.rb
# Gửi thông báo tới người dùng
class PushNotificationService
 def initialize
   # code
 end

 # gửi thông báo
 def notify(record)
   #code
 end
end

Rất đơn giản, muốn sử dụng method notify để send thông báo ở mọi chỗ chúng ta sử dụng

 # app/controllers/test_controller.rb #create
   def create
   ...
   if @test.save
       PushNotificationService.new.notify(@test)
     # code push notification
   else
     # code
  end
  ...

3. Test Service Object

Việc test service object này cũng tương đối đơn giản, giống như chúng ta test cho controller:

require 'rails_helper'

describe UserNotificationService do
  let(:test) { FactoryBot.create(:test) }
  
  subject(:notification) do
    PushNotificationService.new.notify(test)
  end

  context 'when send notification success' do
    it 'send an notification' do
      # code
      notification
    end
  end
  ...
end

4. Khi nào thì nên sử dụng Service Objects

Sự thật là không có một quy tắc cứng nào cho việc sử dụng service objects. Thông thường, các services sẽ tốt hơn ở các hệ thống tầm vừa và lớn. Bất cứ khi nào bạn thấy một đoạn code có thể không thuộc về các thư mục mà bạn đã sử dụng, một ý tưởng tốt để suy nghĩ và xem xét là nó có thể sử dụng một service thay thế. Ví dụ như, một method chứa logic liên quan đến nhiều model, không thuộc về một model riêng biệt nào, hoặc method đó có chứa business logic, hoặc method đó gọi đến một external API chẳng hạn.

5. Kết luận

Service objects là một cách tuyệt vời để tổ chức codes và business logic và nó không hề quá khó. Chúng sẽ làm codes của bạn dễ đọc hơn, dễ bảo trì và test. Vì vậy, về cơ bản, từ bây giờ, hãy thử sử dụng services objects bất cứ khi nào có thể ứng dụng, và ghi nhớ để giữ cho services của bạn gọn gàng và dễ quản lý.

6. Tài liệu tham khảo

1.https://www.netguru.co/blog/service-objects-in-rails-will-help

2.https://reinteractive.com/posts/268-keeping-your-classes-small-and-maintainable-with-service-objects


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí