Facade design pattern trong Ruby on Rails

Cung cấp một Interface thống nhất cho một tập hợp các Interfaces trong một hệ thống con. Facade định nghĩa một Interface cấp cao hơn làm cho hệ thống con dễ sử dụng hơn Và nó là 1 phần của nhóm Structural Design Pattern.

Mình cũng mới tìm hiểu nó, và thấy khá hay ho, nên chia sẽ cùng mọi người ! 😄

Đặt ra vấn đề trong ROR:

Ruby On Rails được xây dựng và hoạt động dựa trên mô hình MVC ( Model-View-Controller ). Và ở đây View có trách nhiệm hiển thị dữ liệu. Model chịu trách nhiệm thao tác dữ liệu, và xử lý Logic, Cuối cùng Controller chịu trách nhiệm kết nối mọi thứ.

Nhưng chúng ta có thể thay trong nhiều dự án ROR thì Controller vẫn khá là yếu vì nó chuẩn bị khá là nhiều dữ liệu để show ra ở View mặc dù chúng ta đã có áp dụng Services để perform một vài hoạt động, hay cấu trúc lại Model để dễ thể hiện ở View hơn. Nhưng đôi khi từng đó là chưa đủ và Controller của chúng ta vẫn Béo lên theo từng ngày. Chẵng hạn như sau (code lượm lặt)

class UsersController < ApplicationController
  def index
    @user = User.new
    @last_active_users = User.active.order(created_at: :desc).limit(10)
    @vip_users_presenter = VipUsersPresenter.new(User.active.vip)
    @messages = current_user.messages
  end
end

Như thế rất là nhiều object được khởi tạo. để có thể được show hay xử lý ở View

Giải pháp của Facade là gì?

Chúng ta có thể sử dụng Facade Desgin Pattern để loại bỏ việc chuẩn bị nhiều data cho tầng View và tạo ra một khuôn mẫu thống nhất và ẩn các thành phần đó đi.

Hãy thử là nhé: tạo 1 folder trong Project như sau app/facades :

# app/facades/users_facade.rb

class UsersFacade
  attr_reader :current_user, :vip_presenter

  def initialize(current_user, vip_presenter=VipUsersPresenter)
    @current_user = current_user
    @vip_presenter = vip_presenter
  end

  def new_user
    User.new
  end

  def last_active_users
    @last_active_users ||= active_users.order(created_at: :desc).limit(10)
  end

  def vip_users
    @vip_users ||= vip_presenter.new(active_users.vip).users
  end

  def messages
    @messages ||= current_user.messages
  end

  private
  def active_users
    User.active
  end
end

Như thế các bạn có thể nhìn thấy rằng:

  • Chúng ta đã chuyển các data chuẩn bị cho View và trong UsersFacade
  • Chúng ta đã có caching một số data trong trường hợp view muốn sử dụng lại.
  • Chúng ta áp dụng một vài cơ chế như includes và ** Dependency injection** với VipUsersPresenter

Và giờ đây Controller của chúng ta lại suy dinh dưỡng như thế này đây:

class UsersController < ApplicationController
  def index
    @user_facade = UsersFacade.new(current_user)
  end
end

và áp dụng nó ở View thế này:

<%= render @user_facade.last_active_users %>
<%= render @user_facade.messages %>
<%= render 'users/form', user: @user_facade.new_user %>
<%= render @user_facade.vip_users %>

Tóm lại nó là gì? và để làm gì?

Facade pattern có thể giúp chúng ta là đơn giản cái Controller trong trường hợp nó ngày càng béo ra vì dự án ngày càng lớn dần. Nó dễ hơn trong việc Test vì dữ liệu được đóng gói lại theo 1 chuẩn.

Bên cạnh đó nó cũng có một số hạn chế Facade của bạn không được quá lớn. Bạn không nên chỉ sử dụng một Facade vì nó quá lớn sẽ khó tái sử dụng. Vì thế bạn nên thiết kế riêng từ Facade cho từng Controller


All Rights Reserved