खोज…


टिप्पणियों

ऐसा करना एक साधारण बात लगती है, लेकिन जब आप कक्षाएं आकार में गुब्बारा करने लगते हैं तो आप आभारी होंगे कि आपने उन्हें व्यवस्थित करने के लिए समय लिया।

मॉडल वर्ग

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 , Cart और Product मॉडल शामिल हैं - इसे कहां रहना चाहिए?

एक वर्ग जो ActiveRecord::Base से विरासत में मिला है ActiveRecord::Base पहले से ही बहुत सारी जिम्मेदारियां हैं। यह क्वेरी इंटरफ़ेस, संघों और मान्यताओं को संभालता है। यदि आप अपने मॉडल में और भी अधिक कोड जोड़ते हैं तो यह जल्दी से सैकड़ों सार्वजनिक तरीकों के साथ एक अप्रत्याशित गड़बड़ हो जाएगा।

एक सेवा सिर्फ एक नियमित रूबी वस्तु है। इसकी कक्षा को किसी विशिष्ट वर्ग से विरासत में प्राप्त नहीं होना है। इसका नाम एक क्रिया वाक्यांश, उदाहरण के लिए है CreateUserAccount बजाय UserCreation या UserCreationService । यह ऐप / सर्विसेज डायरेक्टरी में रहता है। आपको यह निर्देशिका स्वयं बनानी होगी, लेकिन आपके लिए रेलें ऑटोलैड क्लासेस के अंदर होंगी।

एक सेवा वस्तु एक काम करती है

एक सेवा ऑब्जेक्ट (उर्फ विधि ऑब्जेक्ट) एक क्रिया करता है। यह उस क्रिया को करने के लिए व्यावसायिक तर्क रखता है। यहाँ एक उदाहरण है:

# 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/invite/accept.rb Invite::Accept app/services/invite/accept.rb को परिभाषित करेगा Invite::Accept app/services/invite/accept.rb को Invite::Accept हुए Invite::Accept app/services/invite/create.rb Invite::Create
  • सेवाएं एक क्रिया से शुरू होती हैं (और सेवा के साथ समाप्त नहीं होती हैं): ApproveTransaction , SendTestNewsletter , ImportUsersFromCsv
  • सेवाएँ call विधि का जवाब देती हैं। मैंने पाया कि एक और क्रिया का उपयोग करने से यह थोड़ा बेमानी हो जाता है: ApproveTransaction.approve() अच्छी तरह से नहीं पढ़ता है। इसके अलावा, call विधि lambda , procs और विधि ऑब्जेक्ट्स के लिए वास्तविक विधि है।

लाभ

सेवा ऑब्जेक्ट दिखाते हैं कि मेरा आवेदन क्या करता है

मैं तो बस सेवाओं निर्देशिका पर नज़र देखने के लिए क्या कर सकते हैं अपने आवेदन करता है: ApproveTransaction , CancelTransaction , BlockAccount , SendTransactionApprovalReminder ...

एक सेवा वस्तु में एक त्वरित नज़र और मुझे पता है कि व्यापार तर्क क्या शामिल है। मुझे नियंत्रकों, ActiveRecord मॉडल कॉलबैक और पर्यवेक्षकों के माध्यम से जाने की ज़रूरत नहीं है कि यह समझने के लिए कि "लेनदेन को मंजूरी देना" क्या शामिल है।

क्लीन-अप मॉडल और कंट्रोलर

नियंत्रकों ने अनुरोधों (पैरामेट्स, सत्र, कुकीज़) को तर्कों में बदल दिया, उन्हें सेवा में नीचे भेज दिया और सेवा की प्रतिक्रिया के अनुसार पुनर्निर्देशित या प्रस्तुत किया।

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

यह परीक्षण करने और बनाए रखने के लिए मॉडल और नियंत्रकों को बहुत आसान बनाता है!

सर्विस क्लास का उपयोग कब करें

जब कोई कार्रवाई इनमें से एक या अधिक मानदंडों को पूरा करती है, तो सेवा ऑब्जेक्ट के लिए पहुंचें:

  • कार्रवाई जटिल है (जैसे एक लेखांकन अवधि के अंत में पुस्तकों को बंद करना)
  • कार्रवाई कई मॉडलों में पहुंचती है (उदाहरण के लिए, ऑर्डर, क्रेडिट कार्ड और ग्राहक वस्तुओं का उपयोग करके ई-कॉमर्स खरीद)
  • कार्रवाई एक बाहरी सेवा (जैसे सामाजिक नेटवर्क पर पोस्टिंग) के साथ सहभागिता करती है
  • कार्रवाई अंतर्निहित मॉडल की एक मुख्य चिंता नहीं है (जैसे एक निश्चित समय अवधि के बाद पुराने डेटा को स्वीप करना)।
  • कार्रवाई करने के कई तरीके हैं (जैसे एक एक्सेस टोकन या पासवर्ड के साथ प्रमाणित करना)।

सूत्रों का कहना है

एडम Niedzielski ब्लॉग

ब्रू हाउस ब्लॉग

कोड जलवायु ब्लॉग



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow