팩워크 에피소드 1을 통한 Ruby on Rails 모듈화
인간은 많은 시간과 노력을 들이지 않고는 문제의 큰 그림을 보기가 어렵습니다. 이는 특히 크고 복잡한 애플리케이션을 작업할 때 발생합니다....
Rails로 작업할 때 흔히 발생하는 문제는 기능의 로직을 어디에 배치할지 결정하는 것입니다.
로직은 컨트롤러, 모델 또는 운이 좋으면 서비스 객체에 배치되는 경우가 많습니다. 그렇다면 서비스 객체가 있다면 왜 유스케이스가 필요할까요?
이 글에서 이 패턴의 이점을 알아보세요.
사용 사례는 일반적으로 목표를 달성하기 위한 역할과 시스템 간의 상호 작용을 정의하는 작업 또는 이벤트 단계의 목록입니다.
이 패턴은 다양한 방식으로 적용되며 다른 이름으로도 불린다는 점을 언급할 가치가 있습니다. 다음과 같이 찾을 수 있습니다. 인터랙터, 연산자 또는 명령에서, 그러나 Ruby 우리가 함께하는 커뮤니티 사용 사례. 모든 구현은 다르지만 사용자의 시스템 사용 사례를 지원한다는 동일한 목적을 가지고 있습니다.
우리의 프로젝트 를 사용하여 요구 사항을 정의하지 않습니다. 사용 사례와 UML에서 이 패턴은 실용적인 방식으로 비즈니스 로직을 구성하는 데 여전히 유용합니다.
우리의 사용 사례 가 있어야 합니다:
시스템에서 무언가를 구매하려는 사용자의 예를 들어 보겠습니다.
모듈 사용 사례
모듈 구매자
클래스 구매
def initialize(buyer:, cart:)
구매자 = 구매자
@cart = cart
end
def call
return unless check_stock
return unless create_purchase
notify end
private
attr_reader :구매자, :장바구니
def check_stock
Services::CheckStock.call(cart: cart)
end
def create_purchase
Services::CreatePurchase.call(구매자: 구매자, 장바구니: 장바구니).call
end
def notify
Services::NotifyBuyer.call(buyer: buyer)
end
end
end
end
이 그림에서 볼 수 있듯이 코드 예를 들어, 우리는 새로운 사용 사례 구매라고 합니다. 하나의 공개 메서드만 정의했습니다. 통화. 호출 메서드 내부에는 구매를 위한 매우 기본적인 단계가 있으며, 모든 단계는 비공개 메서드로 정의되어 있습니다. 모든 단계는 서비스 객체를 호출하고 있으며, 이렇게 하면 우리의 사용 사례 는 구매를 위한 단계만 정의할 뿐 로직 자체는 정의하지 않습니다. 이를 통해 시스템에서 수행할 수 있는 작업(구매)과 이를 달성하기 위한 단계를 명확하게 파악할 수 있습니다.
이제 첫 번째 사용 사례 컨트롤러에서
컨트롤러 클래스
def purchase
UseCases::Buyer::Purchase.new(
구매자: 구매_파람[:구매자],
cart: purchase_params[:cart])
).call
...
end
...
end
이러한 관점에서 볼 때 사용 사례 는 서비스 객체와 매우 비슷해 보이지만 목적이 다릅니다. 서비스 객체는 낮은 수준의 작업을 수행하고 데이터베이스와 같은 시스템의 다른 부분과 상호 작용하는 반면, 서비스 객체는 사용 사례 생성 새로운 고수준 추상화를 통해 논리적 단계를 정의합니다.
첫 번째 사용 사례 는 작동하지만 더 좋을 수 있습니다. 어떻게 개선할 수 있을까요? 다음과 같이 건조 보석. 이 경우에는 건식 거래.
먼저 기본 클래스를 정의해 보겠습니다.
UseCase 클래스
Dry::트랜잭션 포함
클래스 << self
def call(**args)
new.call(**args)
end
end
end
이렇게 하면 사용 사례 트랜잭션에 속성을 전달하고 이를 사용하는 데 도움이 됩니다. 이제 구매 사용 사례를 다시 정의할 준비가 되었습니다.
모듈 사용 사례
모듈 구매자
클래스 구매
def initialize(buyer:, cart:)
구매자 = 구매자
@cart = cart
end
def call
return unless check_stock
return unless create_purchase
notify
end
private
attr_reader :구매자, :장바구니
def check_stock
Services::CheckStock.call(cart: cart)
end
def create_purchase
Services::CreatePurchase.call(구매자: 구매자, 장바구니: 장바구니).call
end
def notify
Services::NotifyBuyer.call(buyer: buyer)
end
end
end
end
새로운 변경 사항을 통해 단계가 어떻게 정의되는지 명확하게 확인할 수 있으며 성공() 및 실패()를 통해 모든 단계의 결과를 관리할 수 있습니다.
컨트롤러에서 새 사용 사례를 호출하고 최종 결과에 따라 응답을 준비할 준비가 되었습니다.
컨트롤러 클래스
def purchase
UseCases::Buyer::Purchase.new.call(
구매자: 구매_파람[:구매자],
cart: purchase_params[:cart])
) do |result|
result.success do
...
end
result.failure do
...
end
end
...
end
...
end
이 예제는 유효성 검사를 통해 훨씬 더 개선할 수 있지만, 이 패턴의 힘을 보여주기에 충분합니다.
솔직히 말해서 사용 사례 패턴 는 매우 간단하고 서비스 객체와 매우 비슷해 보이지만 이 정도의 추상화 수준은 애플리케이션에 큰 변화를 가져올 수 있습니다.
새로운 개발자가 프로젝트에 합류하여 use_cases 폴더를 열면 첫인상으로 시스템에서 사용 가능한 모든 기능의 목록이 표시되고, 하나의 사용 사례를 열면 로직에 깊이 들어가지 않고도 해당 기능에 필요한 모든 단계를 볼 수 있다고 상상해 보세요. 이러한 질서와 통제감이 이 패턴의 가장 큰 장점입니다.
이것을 도구 상자에 넣어두면 나중에 유용하게 사용할 수 있을 것입니다.