एक सेवा वस्तु एक रूबी वस्तु है जो एक ही क्रिया करती है। यह आपके डोमेन या व्यावसायिक तर्क में एक प्रक्रिया को समाहित करता है। कल्पना कीजिए कि आपको एक काल्पनिक पुस्तकालय अनुप्रयोग में एक पुस्तक उदाहरण बनाने की आवश्यकता है; एक सादे रेल ऐप में, आप निम्न कार्य करेंगे:
class BookController < ApplicationController
def create
Book.new(*args)
end
end
यह साधारण चीजों के लिए ठीक है। हालाँकि, जैसे-जैसे ऐप बढ़ता है, आप इसके चारों ओर बहुत सारे बॉयलरप्लेट के साथ समाप्त हो सकते हैं:
class BookController < ApplicationController
def create
default_args = { genre: find_genre(), author: find_author() }
Book.new(attrs.merge(default_args))
end
private
def find_genre
// ...
end
def find_author
// ...
end
end
सेवा वस्तुएं आपको इस व्यवहार को एक अलग वर्ग में सार करने की अनुमति देती हैं। फिर, आपका कोड फिर से सरल हो जाता है:
class BookController < ApplicationController
def
BookCreator.create_book
end
end
आपको सेवा ऑब्जेक्ट की आवश्यकता क्यों है
रेल को मूल रूप से MVC (जैसे, मॉडल, नियंत्रक, विचार और सहायक) संगठनात्मक संरचना का समर्थन करने के लिए डिज़ाइन किया गया है। यह संरचना सरल अनुप्रयोगों के लिए पर्याप्त है। हालाँकि, जैसे-जैसे आपका एप्लिकेशन बढ़ता है, आप मॉडल और नियंत्रक में डोमेन/व्यावसायिक तर्क देखना शुरू कर सकते हैं। इस तरह के तर्क न तो नियंत्रक या मॉडल से संबंधित होते हैं, इसलिए वे कोड को फिर से उपयोग करने और बनाए रखने में मुश्किल बनाते हैं। रेल सेवा ऑब्जेक्ट एक ऐसा पैटर्न है जो आपको व्यावसायिक तर्क को नियंत्रकों और मॉडलों से अलग करने में मदद कर सकता है, जिससे मॉडल को केवल डेटा स्तर और आपके एपीआई के नियंत्रक प्रवेश बिंदु के रूप में सक्षम किया जा सकता है।
जब हम व्यावसायिक तर्क को समाहित करने के लिए सेवाओं की शुरुआत करते हैं, तो हमें बहुत सारे लाभ मिलते हैं, जिनमें निम्न शामिल हैं:
-
लीन रेल नियंत्रक - नियंत्रक केवल अनुरोधों को समझने और अनुरोध पैरामीटर, सत्र और कुकीज़ को तर्कों में बदलने के लिए ज़िम्मेदार है जो सेवा ऑब्जेक्ट में कार्य करने के लिए पारित किए जाते हैं। नियंत्रक तब सेवा प्रतिक्रिया के अनुसार पुनर्निर्देशित या प्रस्तुत करता है। बड़े अनुप्रयोगों में भी, सेवा वस्तुओं का उपयोग करने वाली नियंत्रक क्रियाएं आमतौर पर कोड की 10 पंक्तियों से अधिक नहीं होती हैं।
-
परीक्षण योग्य नियंत्रक - चूंकि नियंत्रक दुबले होते हैं और सेवा के सहयोगी के रूप में काम करते हैं, इसलिए इसका परीक्षण करना वास्तव में आसान हो जाता है, क्योंकि हम केवल यह जांच सकते हैं कि जब कोई निश्चित क्रिया होती है तो नियंत्रक के भीतर कुछ विधियों को कॉल किया जाता है या नहीं।
-
व्यावसायिक प्रक्रिया का अलग-अलग परीक्षण करने की क्षमता - सेवाओं का परीक्षण करना आसान और तेज़ है क्योंकि वे छोटी रूबी वस्तुएं हैं जिन्हें उनके पर्यावरण से अलग कर दिया गया है। हम सभी सहयोगियों को आसानी से रोक सकते हैं और केवल यह जांच सकते हैं कि हमारी सेवा में कुछ कदम उठाए गए हैं या नहीं।
-
पुन:प्रयोज्य सेवाएं - सेवा वस्तुओं को नियंत्रकों, अन्य सेवा वस्तुओं, कतारबद्ध नौकरियों आदि द्वारा बुलाया जा सकता है।
-
ढांचे और व्यावसायिक डोमेन के बीच अलगाव - रेल नियंत्रक केवल सेवाओं को देखते हैं और उनका उपयोग करके डोमेन ऑब्जेक्ट के साथ इंटरैक्ट करते हैं। युग्मन में यह कमी स्केलेबिलिटी को आसान बनाती है, खासकर जब आप एक मोनोलिथ से एक माइक्रोसर्विस में जाना चाहते हैं। आपकी सेवाओं को आसानी से निकाला जा सकता है और न्यूनतम संशोधन के साथ एक नई सेवा में स्थानांतरित किया जा सकता है।
सेवा ऑब्जेक्ट बनाना
सबसे पहले, आइए एक नए फोल्डर में एक नया BookCreator बनाएँ, जिसे एक काल्पनिक लाइब्रेरी प्रबंधन एप्लिकेशन के लिए ऐप/सेवाएँ कहा जाता है:
$ mkdir app/services && touch app/services/book_creator.rb
इसके बाद, आइए अपने सभी तर्कों को एक नए रूबी वर्ग के अंदर डंप करें:
# app/services/book_creator.rb
class BookCreator
def initialize(title:, description:, author_id:, genre_id:)
@title = title
@description = description
@author_id = author_id
@genre_id = genre_id
end
def create_book
Boook.create!(
title: @title
description: @description
author_id: @author_id
genre_id: @genre_id
)
rescue ActiveRecord::RecordNotUnique => e
# handle duplicate entry
end
end
end
फिर, हम सर्विस ऑब्जेक्ट को कंट्रोलर में या एप्लिकेशन के भीतर कहीं भी कॉल कर सकते हैं:
class BookController < ApplicationController
def create
BookCreator.new(title: params[:title], description: params[:description], author_id: params[:author_id], genre_id: params[:genre_id]).create_book
end
end
सर्विस ऑब्जेक्ट सिंटेक्टिक शुगर
हम BookCreator.new(arguments).create
. को सरल बना सकते हैं BookCreator
. को इंस्टेंट करने वाली क्लास मेथड को जोड़कर चेन और create
. को कॉल करता है हमारे लिए विधि:
# app/services/book_creator.rb
class BookCreator
def initialize(title:, description:, author_id:, genre_id:)
@title = title
@description = description
@author_id = author_id
@genre_id = genre_id
end
def call(*args)
new(*args).create_book
end
private
def create_book
Boook.create!(
title: @title
description: @description
author_id: @author_id
genre_id: @genre_id
)
rescue ActiveRecord::RecordNotUnique => e
# handle duplicate entry
end
end
end
नियंत्रक में, पुस्तक निर्माता को अब इस प्रकार कहा जा सकता है:
class BookController < ApplicationController
def create
BookCreator.call(
title: params[:title],
description: params[:description],
author_id: params[:author_id],
genre_id: params[:genre_id])
end
end
हमारे कोड को DRY (डोंट रिपीट योरसेल्फ) रखने के लिए और अन्य सेवा वस्तुओं के साथ इस व्यवहार का पुन:उपयोग करने के लिए, हम call
को सार कर सकते हैं आधार ApplicationService
. में विधि वह वर्ग जिससे प्रत्येक सेवा वस्तु इनहेरिट की जाएगी:
class ApplicationService
self.call(*args)
new(*args).call
end
end
इस कोड के साथ, हम BookCreator
. को रिफलेक्टर कर सकते हैं ApplicationService
. से इनहेरिट करने के लिए :
# app/services/book_creator.rb
class BookCreator < ApplicationService
def initialize(title:, description:, author_id:, genre_id:)
@title = title
@description = description
@author_id = author_id
@genre_id = genre_id
end
def call
create_book
end
private
def create_book
# ...
end
end
BusinessProcess Gem का उपयोग करके सेवा ऑब्जेक्ट बनाना
BusinessProcess रत्न के साथ, आपको एक मूल अनुप्रयोग सेवा वर्ग बनाने या initialize
को परिभाषित करने की आवश्यकता नहीं है विधि क्योंकि मणि में इन सभी विन्यासों को बनाया गया है। आपकी सेवा वस्तु को केवल BusinessProcess::Base
. से इनहेरिट करना है ।
अपनी रत्न फ़ाइल में, निम्नलिखित जोड़ें:
gem 'business_process'
और फिर bundle
चलाएं अपने टर्मिनल में कमांड करें
class BookCreator < BusinessProcess::Base
# Specify requirements
needs :title
needs :description
needs :author_id
needs :genre_id
# Specify process (action)
def call
create_book
end
private
def create_book
# ...
end
end
गुड सर्विस ऑब्जेक्ट बनाने के लिए गाइड
एक सार्वजनिक तरीका
एक सेवा वस्तु को एक व्यावसायिक क्रिया करने और इसे अच्छी तरह से करने के लिए माना जाता है, इसलिए उसे ऐसा करने के लिए केवल एक ही सार्वजनिक विधि का खुलासा करना चाहिए। अन्य विधियां निजी होनी चाहिए और सार्वजनिक विधि द्वारा बुलाई जानी चाहिए। आप जो चाहें सार्वजनिक पद्धति का नाम चुन सकते हैं, जब तक कि नामकरण सभी सेवा वस्तुओं के अनुरूप हो। हमारे उदाहरण में, हमने इसे call
. नाम दिया है . अन्य पारंपरिक नाम हैं perform
और execute
।
सेवा ऑब्जेक्ट को उनकी भूमिका के अनुसार नाम दें
सर्विस ऑब्जेक्ट का नाम इंगित करना चाहिए कि यह क्या करता है। "या" और "एर" के साथ समाप्त होने वाले शब्दों के साथ सेवा वस्तुओं का नामकरण करने का एक लोकप्रिय तरीका है। उदाहरण के लिए, यदि सेवा वस्तु का कार्य पुस्तक बनाना है, तो उसे BookCreator नाम दें, और यदि कार्य किसी पुस्तक को पढ़ना है, तो उसे BookReader नाम दें।
सेवा वस्तुओं को सीधे न करें
सेवा वस्तुओं को कॉल करने के संकेतन को छोटा करने के लिए सिंटैक्टिक चीनी पैटर्न या बिजनेसप्रोसेस जैसे रत्नों का उपयोग करें। इस दृष्टिकोण का उपयोग करने से आप BookCreator.new(*args).call
. को सरल बना सकेंगे या BookCreator.new.call(*args)
BookCreator.call(*args),
. में जो छोटा और अधिक पठनीय है।
नामस्थान में समूह सेवा ऑब्जेक्ट
सेवा वस्तुओं का परिचय, विशेष रूप से एक बड़े अनुप्रयोग में, इसका मतलब है कि आप एक सेवा वस्तु से दस सेवा वस्तुओं तक बढ़ेंगे। कोड संगठन को बेहतर बनाने के लिए, सामान्य सेवा वस्तुओं को नामस्थानों में समूहित करना एक अच्छा अभ्यास है। उदाहरण के लिए, लाइब्रेरी एप्लिकेशन में, हम सभी पुस्तक-संबंधित सेवाओं को एक साथ समूहित करेंगे और सभी लेखक-संबंधित सेवाओं को एक अलग नाम स्थान में समूहित करेंगे। हमारी फ़ोल्डर संरचना अब इस तरह दिखेगी:
services
├── application_service.rb
└── book
├── book_creator.rb
└── book_reader.rb
हमारी सेवा वस्तुएं इस तरह दिखाई देंगी:
# services/book/book_creator.rb
module Book
class BookCreator < ApplicationService
...
end
end
# services/twitter_manager/book_reader.rb
module Book
class BookReader < ApplicationService
...
end
end
हमारी कॉल अब Book::BookCreator.call(*args) and Book::BookReader.call(*args)
हो जाएंगी। ।
प्रति सेवा वस्तु के लिए एक जिम्मेदारी
एक सेवा वस्तु का होना जो एक से अधिक कार्य करता है, सेवा वस्तुओं की "व्यावसायिक क्रिया" मानसिकता के विरुद्ध जाता है। परंपरागत रूप से, एक सामान्य सेवा वस्तु जो कई क्रियाएं करती है, को हतोत्साहित किया जाता है। यदि आप सेवा वस्तुओं के बीच कोड साझा करना चाहते हैं, तो आधार या सहायक मॉड्यूल बनाएं और अपनी सेवा वस्तुओं में शामिल करने के लिए मिक्सिन का उपयोग करें।
बचाव अपवाद और कस्टम अपवाद बढ़ाएँ
सर्विस ऑब्जेक्ट का उद्देश्य इसके अंदर कार्यान्वयन विवरण को समाहित करना है, जैसे कि तृतीय-पक्ष सेवाओं या पुस्तकालयों के बीच बातचीत या Rails ActiveRecord के साथ डेटाबेस हेरफेर। यदि कोई त्रुटि होती है, जैसे ActiveRecord::RecordNotUnique, ActiveRecord के साथ इंटरैक्ट करते समय, सेवा को अपवाद को ठीक से बचाने की आवश्यकता होती है। कॉल स्टैक को प्रचारित करने के लिए त्रुटियों की अनुमति नहीं दी जानी चाहिए। अगर इसे रेस्क्यू ब्लॉक में हैंडल नहीं किया जा सकता है, तो उस सर्विस ऑब्जेक्ट के लिए विशिष्ट कस्टम-डिफ़ाइंड अपवाद उठाएँ।
निष्कर्ष
जब आप अपने एप्लिकेशन में नई सुविधाएँ जोड़ते हैं तो सर्विस ऑब्जेक्ट पैटर्न आपके एप्लिकेशन के समग्र डिज़ाइन में बहुत सुधार कर सकता है। यह आपके कोडबेस को अधिक अभिव्यंजक, बनाए रखने में आसान और परीक्षण के लिए कम दर्दनाक बना देगा।