수색…


비고

이것은 간단한 일처럼 보입니다. 그러나 수업을 시작할 때 크기가 급상승 할 때 시간을내어 정리해 주셔서 감사 할 것입니다.

모델 클래스

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :comments

  validates :user, presence: true
  validates :title, presence: true, length: { in: 6..40 }

  scope :topic, -> (topic) { joins(:topics).where(topic: topic) }

  before_save :update_slug
  after_create :send_welcome_email

  def publish!
    update(published_at: Time.now, published: true)
  end

  def self.find_by_slug(slug)
    find_by(slug: slug)
  end

  private

  def update_slug
    self.slug = title.join('-')
  end

  def send_welcome_email
    WelcomeMailer.welcome(self).deliver_now
  end
end

모델은 일반적으로 다음을 담당합니다.

  • 관계 설정
  • 데이터 검증 중
  • 범위 및 메서드를 통해 데이터에 액세스 제공
  • 데이터 지속성과 관련된 작업 수행

최상위 레벨에서 모델은 도메인 개념을 설명하고 지속성을 관리합니다.

서비스 클래스

컨트롤러는 애플리케이션의 시작점입니다. 그러나 유일한 진입 점이 아닙니다. 내 논리를 다음에서 액세스 할 수있게하려고합니다.

  • 갈퀴 작업
  • 배경 작업
  • 콘솔
  • 검사들

컨트롤러에 논리를 삽입하면이 모든 장소에서 액세스 할 수 없습니다. 그럼 "스키니 컨트롤러, 뚱뚱한 모델"접근 방식을 시도하고 로직을 모델로 옮겨 봅시다. 그러나 어느 것? 주어진 로직에 User , CartProduct 모델이 포함되는 경우 - 어디서 살면됩니까?

ActiveRecord::Base 로부터 상속받은 클래스는 이미 많은 책임이 있습니다. 쿼리 인터페이스, 연결 및 유효성 검사를 처리합니다. 모델에 더 많은 코드를 추가하면 수백 가지의 공용 메소드에서 유지 관리가 쉽지 않습니다.

서비스는 단지 일반적인 Ruby 객체입니다. 이 클래스는 특정 클래스에서 상속 할 필요가 없습니다. 그 이름은 동사입니다 (예 : UserCreation 또는 UserCreationService 대신 CreateUserAccount . 그것은 app / services 디렉토리에 있습니다. 이 디렉토리는 직접 만들어야하지만, Rails는 자동으로 클래스를 자동로드합니다.

서비스 객체는 한 가지 작업을 수행합니다.

서비스 객체 (메서드 객체라고도 함)는 하나의 작업을 수행합니다. 해당 작업을 수행 할 비즈니스 논리를 보유합니다. 다음은 그 예입니다.

# app/services/accept_invite.rb
class AcceptInvite
  def self.call(invite, user)
    invite.accept!(user)
    UserMailer.invite_accepted(invite).deliver
  end
end

내가 따라하는 세 가지 규칙은 다음과 같습니다.

서비스는 app/services directory 아래에 app/services directory . 비즈니스 로직이 많은 도메인에 하위 디렉토리를 사용하는 것이 좋습니다. 예를 들면 :

  • app/services/invite/accept.rb 파일은 Invite::Accept 를 정의하고 app/services/invite/create.rbInvite::Create 정의합니다.
  • 서비스는 동사로 시작하며 서비스로 끝나지 않습니다 : ApproveTransaction , SendTestNewsletter , ImportUsersFromCsv
  • 서비스가 call 메소드에 응답합니다. 다른 동사를 사용하면 중복 된 것으로 나타났습니다. ApproveTransaction.approve() 는 잘 읽지 않습니다. 또한, call 메소드는 lambda , procs 및 메소드 객체에 대한 사실상의 메소드입니다.

은혜

서비스 객체는 내 애플리케이션에서 수행하는 작업을 보여줍니다.

내 응용 프로그램이 무엇인지보기 위해 서비스 디렉토리를 살펴볼 수 있습니다 : ApproveTransaction , CancelTransaction , BlockAccount , SendTransactionApprovalReminder ...

서비스 오브젝트에 대한 간략한 조사와 관련된 비즈니스 로직을 알고 있습니다. 컨트롤러, ActiveRecord 모델 콜백 및 옵저버를 통해 "트랜잭션 승인"이 무엇을 의미하는지 이해할 필요가 없습니다.

클린업 모델 및 컨트롤러

컨트롤러는 요청 (params, session, cookies)을 인수로 변환하여 서비스로 전달하고 서비스 응답에 따라 리디렉션하거나 렌더링합니다.

class InviteController < ApplicationController
 def accept
    invite = Invite.find_by_token!(params[:token])
    if AcceptInvite.call(invite, current_user)
      redirect_to invite.item, notice: "Welcome!"
    else
      redirect_to '/', alert: "Oopsy!"
    end
  end
end

모델은 연관, 범위, 유효성 검사 및 지속성 만 처리합니다.

class Invite < ActiveRecord::Base
  def accept!(user, time=Time.now)
    update_attributes!(
      accepted_by_user_id: user.id,
      accepted_at: time
    )
  end
end

따라서 모델과 컨트롤러를 테스트하고 유지 관리하는 것이 훨씬 쉬워집니다!

서비스 클래스 사용시기

작업이 다음 기준 중 하나 이상을 충족하면 서비스 개체 도달 범위 :

  • 행동은 복잡합니다 (예 : 회계 기간 말에 책을 닫음)
  • 이 작업은 여러 모델에 걸쳐 이루어집니다 (예 : Order, CreditCard 및 Customer 객체를 사용한 전자 상거래 구매)
  • 액션은 외부 서비스와 상호 작용합니다 (예 : 소셜 네트워크에 게시)
  • 동작은 기본 모델의 핵심 관심사는 아닙니다 (예 : 일정 기간이 지나면 오래된 데이터를 청소).
  • 액션을 수행하는 방법에는 여러 가지가 있습니다 (예 : 액세스 토큰 또는 비밀번호로 인증).

출처

Adam Niedzielski 블로그

BREW 하우스 블로그

코드 기후 블로그



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow