7 Design Patterns to Refactor MVC Components in Rails

Làm thế nào để các thành phần MVC trở nên đơn giản

Để khiến cho Models, Views, Controllers trở nên đơn giản và chuẩn hóa, chúng ta phải liên tục refactor hay tái cấu trúc trên code đã được viết. Trong quá trình tái cấu trúc sẽ không được thay đổi bất kỳ các phản hồi tương tác với bất kỳ hành động nào của người dùng cuối, việc làm đó sẽ giữ cho code dễ đọc, dễ bảo trì, dễ dàng để kiểm thử đem lại nhiều lợi ích cho lập trình viên Tái cấu trúc lại code được thực hiện dựa trên nhiều những quy ước, chúng ta có thể sử dụng một vài mẫu thiết kế hay design patterns bao gồm

  • Service Objects (and Interactor Objects)
  • Value Objects
  • Form Objects
  • Query Objects
  • View Objects (Serializer/Presenter)
  • Policy Objects
  • Decorators Chúng ta sẽ đi vào từng mẫu thiết kế trên để có thể tái cấu trúc code được tốt hơn.

Service Objects (and Interactor Objects)

Service Objects được tạo khi một hoạt động:

  • phức tạp (giống như tính toán lương của nhân viên)
  • kết nối APIs của các dịch vụ bên ngoài
  • không thuộc về một model (ví dụ như việc xóa dữ liệu thừa)
  • sử dụng nhiều models (ví dụ như nhập dữ liệu từ một tệp vào nhiều model)

Value Objects

Mẫu thiết kế Value Object khuyến khích hướng tới đối tượng object đơn giản (thường thì chỉ chứa giá trị gửi đi) và bạn có thể so sánh chúng dựa trên một logic hay một thuộc tính đặc biệt đơn giản (không phải dựa trên định danh). Ví dụ của values objestc là đối tượng đại diện cho một giá tiền theo đơn vị nào đó. Chúng ta có thể so sánh chúng bằng quy đổi trên một đơn vị chung.

Form Objects

Form Objects là một mẫu thiết kế về sự đóng gói logic liên quan tới xác nhận và đảm bảo dữ liệu. Ví dụ chúng ta có User và Admin, nếu model chứa toàn bộ validation logic, chúng ta sẽ không có thể sử dụng lại chúng cho Admin, khi đó FormObject sẽ được tạo chứa toàn bộ logic validation và model sẽ không còn có trách nhiệm validate dữ liệu nữa. Ban đầu chúng ta có controller và model user như sau

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)

    if @user.save
      render json: @user
    else
      render json: @user.error, status: :unprocessable_entity
    end
  end

  private

  def user_params
    params
      .require(:user)
      .permit(:email, :full_name, :password, :password_confirmation)
  end
end

class User < ActiveRecord::Base
  EMAIL_REGEX = /@/ # Some fancy email regex

  validates :full_name, presence: true
  validates :email, presence: true, format: EMAIL_REGEX
  validates :password, presence: true, confirmation: true
end

Một giải pháp là đưa validation logic vào riêng một lớp có tên là UserForm

class UserForm
  EMAIL_REGEX = // # Some fancy email regex

  include ActiveModel::Model
  include Virtus.model

  attribute :id, Integer
  attribute :full_name, String
  attribute :email, String
  attribute :password, String
  attribute :password_confirmation, String

  validates :full_name, presence: true
  validates :email, presence: true, format: EMAIL_REGEX
  validates :password, presence: true, confirmation: true

  attr_reader :record

  def persist
    @record = id ? User.find(id) : User.new

    if valid?
      @record.attributes = attributes.except(:password_confirmation, :id)
      @record.save!
      true
    else
      false
    end
  end
end

Sau đó chúng ta có thể sử dụng UserForm cho controller như sau

class UsersController < ApplicationController
  def create
    @form = UserForm.new(user_params)

    if @form.persist
      render json: @form.record
    else
      render json: @form.errors, status: :unpocessably_entity
    end
  end

  private

  def user_params
    params.require(:user)
          .permit(:email, :full_name, :password, :password_confirmation)
  end
end

Và User model không còn trách nhiệm validate dữ liệu nữa

class User < ActiveRecord::Base
end

Như vậy việc này sẽ giúp cho model trở nên đơn giản hơn, validate logic có thể sử dụng lại được.

Query Objects

Query Objects là một mẫu thiết kế cho phép chúng ta trích xuất query logic từ controller và model thành lớp có thể dùng lại được.

View Objects (Serializer/Presenter)

Một View Objest cho phép chúng ta lấy dữ liệu và tính toán những gì cần cho việc hiển thị một đối tượng của Model lên View, ví dụ như dữ liệu hiển thị lên một trang HTML hay một JSON response từ một API endpoint, việc này không phải của Controller hay Model

Policy Objects

Mẫu thiết kế Policy Objects gần giống với Service Object, nhưng nó chịu trách nhiệm để đọc trong khi Service Object có trách nhiệm để ghi. Policy Objects chứa những quy tắc phức tạp và có thể dễ dàng thay thế bởi Policy Objects với quy tắc khác. Ví dụ chúng ta có thể kiểm tra nếu một user là khách thì có thể lấy về một phần tài nguyên sử dụng bằng một Policy Object cho khách riêng. Nếu user là một admin, chúng ta có thể dễ dàng thay thay thế policy objects này bằng policy object khác chứa quy luật của admin.

Decorators

Mẫu Decorator cho phép chúng ta thêm vào bất kỳ các hành vi tới riêng một đối tượng mà không ảnh hưởng tới đối tượng khác cùng lớp với nó. Mẫu thiết kế này được sử dụng rất rộng rãi để chia các chức năng trên lớp khác nhau, và là một thay thế cho sự phân lớp để đảm bảo theo quy tắc Single Responsibility Principle.

Tổng kết

Như vậy, chúng ta có thể hiểu qua được các khái niệm để giúp chúng ta có thể tái cấu trúc lại code. Các ví dụ khi sử dụng từng mẫu thiết kế trên bạn có thể tham khảo ở liên kết bên dưới. Có nhiều cách để chúng ta có thể làm điều nay nhưng hãy cẩn trọng khi bạn mới bắt đầu với lập trình.

Refs

7 Design Patterns to Refactor MVC Components in Rails


All Rights Reserved