Rails의 양식 개체 패턴: YAAF를 사용하여 모든 것에 대한 서비스 생성 중지

대형 컨트롤러 작업



대형 컨트롤러 방법을 얼마나 많이 접하셨습니까? 당신이 나처럼 운이 좋다면 아마도 여러 번일 것입니다.

긴 컨트롤러 리팩토링을 시작하는 가장 일반적인 방법 중 하나는 코드를 서비스로 옮기는 것입니다.

서비스는 훌륭하고 원자적인 방식으로 코딩하면 테스트하고 이해하기 쉽습니다. 그러나 문제는 서비스를 사용할 때 스위스 군용 칼을 사용하는 것과 같습니다.

-"Hey, I don't know how to properly refactor this piece of code" -"Dude, just do a new service"



하지만 아니요! 새로운 서비스를 만드는 것이 항상 최선의 선택은 아닙니다. 어떤 경우에는 바퀴를 재발명하고 있으며 이미 우리의 요구에 맞는 패턴이 있을 수 있습니다.

그래서 여기에 YAAF가 등장합니다. 우리의 하루를 구하는 또 다른 활성 형식입니다. YAAF는 Rails를 사용하여 쉽고 친숙한 방식으로 양식 개체를 만들 수 있는 보석입니다. ActiveRecordActiveModel 기능을 사용하여 완전히 구성 가능한 상태로 유지하면서 Rails 모델처럼 작동하는 양식 개체를 제공합니다.

YAAF는 언제 사용합니까?



데이터베이스에 새 게시물을 저장하는 API 엔드포인트가 있다고 가정해 보겠습니다. 게시물에는 제목, 본문, 게시자가 있으며 태그와 범주도 있을 수 있습니다.

게시자가 게시물을 보내는 즉시 태그 및 카테고리를 생성할 수 있습니다. (만약 우리의 입력이 정확한 태그나 카테고리를 찾지 못한다면, 사용자가 새로운 태그나 카테고리를 쓰도록 할 것입니다).

따라서 최악의 시나리오에서 컨트롤러는 다음과 같을 수 있습니다.


class Api::V1::PostsController < Api::V1::ApiController
  def index
    @posts = Post.all
  end

  def create
    ActiveRecord::Base.transaction do
      @post = Post.new(post_params)
      @post.tags = params[:tags].map do |tag|
        tag[:id].present? ? Tag.find(tag[:id]) : Tag.find_or_create_by(name: tag[:name])
      end
      if params[:category_name].present?
        category = Category.create!(name: params[:category_name])
        @post.category = category
      end
      @post.save!
    end
  end


  def post_params
    params.require(:post).permit(:title, :body, :publisher_id, :category_id)
  end
end


끔찍해 보이죠? 아마도 그 코드 조각에 대한 첫 번째 생각은 게시물 생성을 PostCreationService 라는 서비스로 이동하는 리팩토링을 만드는 것입니다. 이것은 유용할 수 있으며 향후 시스템의 다른 부분에서 사용될 수 있습니다. 하지만 바퀴를 재발명하는 것에 대해 무엇을 말했습니까?

YAAF를 사용할 때 게시물 생성의 모든 로직과 관련 모델을 내부에 캡슐화할 새PostForm 클래스를 만들어야 합니다. 그리고 그것을 구현하는 것은 매우 간단합니다! 이 코드를 보세요.


# app/forms/post_form.rb

class PostForm < ApplicationForm
  attr_accessor :post, :category_name, :tags
  validate :amount_of_tags

  def initialize(args = {})
    super(args)
    @models = [new_post, category, post_tags].flatten.compact
  end

  def new_post
    @new_post ||= Post.new(post).tap do |post|
      post.category = category
      post.tags = post_tags
    end
  end

  def category
    return [] if category_name.blank?

    @category ||= Category.find_or_initialize_by(name: category_name)
  end

  def post_tags
    return [] if tags.blank?

    @post_tags ||= tags.map do |tag|
      tag[:id].present? ? Tag.find(tag[:id]) : Tag.find_or_initialize_by(name: tag[:name])
    end
  end

  private

  def amount_of_tags
    return if tags.size.between?(1, 3)

    errors.add(:base, "You can't assign more than three tags to a post")
  end
end


참고: YAAF가 양식 개체에 비즈니스 규칙을 캡슐화하는 데 도움이 되므로 amount_of_tags 라는 맞춤 유효성 검사도 추가했습니다.

그런 다음 컨트롤러에는 다음이 있습니다.


class Api::V1::PostsController < Api::V1::ApiController
  def index
    @posts = Post.all
  end

  def create
    form = PostForm.new(post_form_params)
    form.save!
    @post = form.post
  end

  private

  def post_form_params
    params.permit(:category_name, tags: %i[id name], post: %i[title body publisher_id category_id])
  end
end


도움말: ApplicationForm에서 상속되는 YAAF::Form를 갖는 것은 좋은 습관입니다.

그게 다야, 이제 게시물/태그/카테고리의 모든 지속성 로직을 캡슐화하는 PostForm가 있어 컨트롤러와 모델을 깔끔하게 유지하고 따르기 쉬운 코드를 제공합니다.

또 다른 좋은 점은 YAAF가 ActiveModel 모델과 유사한 API를 제공하므로 서로 바꿔서 취급할 수 있다는 것입니다.

서비스나 PORO가 아닌 이유는 무엇입니까?



  • 팀으로 작업하는 경우 맞춤형 서비스 또는 PORO를 만드는 것이 무질서할 수 있습니다.
  • YAAF를 사용하면 양식 패턴을 쉽게 적용할 수 있습니다.
  • YAAF의 길이는 64줄에 불과합니다.
  • 잘 테스트되고 유지 관리됩니다.
  • 비즈니스 로직을 배치할 더 나은 위치를 제공하여 모델, 보기 및 컨트롤러를 얇게 유지하는 데 도움이 됩니다. 결국에는 코드베이스의 품질이 향상되고 유지 관리 및 확장이 더 쉬워집니다.
  • 그리고 많이more .

  • 요약



    여기까지 했다면 이 문서가 YAAF를 프로젝트에 통합하는 데 도움이 되길 바랍니다. 또한 FormObject 패턴을 사용하여 코드를 더욱 개선하는 데 도움이 되었기를 바랍니다. 더 많은 예를 볼 수 있습니다here. YAAF는 오픈소스이며 새로운 기여를 받을 수 있습니다.

    그래서 그것을 확인하고 당신의 생각을 참조하십시오!

    좋은 웹페이지 즐겨찾기