I want to understand basic, abstract and correct architectural approach for networking applications in iOS
: एप्लिकेशन आर्किटेक्चर के निर्माण के लिए कोई "सर्वश्रेष्ठ" या "सबसे सही" दृष्टिकोण नहीं है। यह एक बहुत ही रचनात्मक काम है। आपको हमेशा सबसे सीधा और एक्स्टेंसिबल आर्किटेक्चर चुनना चाहिए, जो किसी भी डेवलपर के लिए स्पष्ट होगा, जो आपकी परियोजना पर या आपकी टीम में अन्य डेवलपर्स के लिए काम करना शुरू कर देगा, लेकिन मैं मानता हूं, कि "अच्छा" और "बुरा" हो सकता है " आर्किटेक्चर।
आपने कहा: collect the most interesting approaches from experienced iOS developers
मुझे नहीं लगता कि मेरा दृष्टिकोण सबसे दिलचस्प या सही है, लेकिन मैंने इसे कई परियोजनाओं में इस्तेमाल किया है और इससे संतुष्ट हूं। यह उन लोगों का एक संकर दृष्टिकोण है, जिनका आपने ऊपर उल्लेख किया है, और मेरे अपने शोध प्रयासों से सुधार के साथ भी। मैं इमारत के दृष्टिकोण की समस्याओं में दिलचस्प हूं, जो कई प्रसिद्ध पैटर्न और मुहावरों को जोड़ती है। मुझे लगता है कि बहुत से फाउलर के एंटरप्राइज पैटर्न को मोबाइल एप्लिकेशन पर सफलतापूर्वक लागू किया जा सकता है। यहां सबसे दिलचस्प लोगों की एक सूची है, जिसे हम एक iOS एप्लिकेशन आर्किटेक्चर ( मेरी राय में ) बनाने के लिए आवेदन कर सकते हैं : सेवा परत , कार्य की इकाई , दूरस्थ मुखौटा , डेटा ट्रांसफर ऑब्जेक्ट , गेटवे , लेयर सुपरटाइप , विशेष मामला , डोमेन मॉडल । आपको हमेशा एक मॉडल परत को सही ढंग से डिज़ाइन करना चाहिए और हमेशा दृढ़ता के बारे में मत भूलना (यह आपके ऐप के प्रदर्शन को काफी बढ़ा सकता है)। आप इसके लिए उपयोग कर सकते हैं Core Data
। लेकिन आपको यह नहीं भूलना चाहिए , कि Core Data
यह ओआरएम या डेटाबेस नहीं है, बल्कि इसके अच्छे विकल्प के रूप में दृढ़ता के साथ एक वस्तु ग्राफ प्रबंधक है। इसलिए, बहुत बार Core Data
आपकी आवश्यकताओं के लिए बहुत भारी हो सकता है और आप नए समाधानों को देख सकते हैं जैसे कि Realm और Couchbase Lite , या कच्चे SQLite या LevelDB के आधार पर अपनी खुद की हल्की वस्तु मैपिंग / दृढ़ता परत का निर्माण करें।। इसके अलावा, मैं आपको सलाह देता हूं कि आप स्वयं को डोमेन संचालित डिजाइन और CQRS से परिचित कराएं ।
सबसे पहले, मुझे लगता है, हमें नेटवर्किंग के लिए एक और परत बनानी चाहिए , क्योंकि हम वसा नियंत्रक या भारी, अभिभूत मॉडल नहीं चाहते हैं। मैं उन fat model, skinny controller
चीजों में विश्वास नहीं करता । लेकिन मैं विश्वास है में skinny everything
, दृष्टिकोण, क्योंकि कोई वर्ग वसा होना चाहिए, कभी। सभी नेटवर्किंग को आमतौर पर व्यावसायिक तर्क के रूप में अमूर्त किया जा सकता है, फलस्वरूप हमारे पास एक और परत होनी चाहिए, जहां हम इसे रख सकते हैं। सेवा परत वह है जो हमें चाहिए:
It encapsulates the application's business logic, controlling transactions
and coordinating responses in the implementation of its operations.
हमारे MVC
क्षेत्र Service Layer
में डोमेन मॉडल और नियंत्रकों के बीच मध्यस्थ की तरह कुछ है। MVCS नामक इस दृष्टिकोण की एक समान भिन्नता है जहाँ Store
वास्तव में हमारी Service
परत है। Store
मॉडल उदाहरणों को प्रस्तुत करता है और नेटवर्किंग, कैशिंग आदि को संभालता है। मैं यह उल्लेख करना चाहता हूं कि आपको अपनी सेवा परत में अपने सभी नेटवर्किंग और व्यावसायिक तर्क नहीं लिखना चाहिए । यह भी एक बुरा डिजाइन माना जा सकता है। अधिक जानकारी के लिए एनीमिक और रिच डोमेन मॉडल देखें। कुछ सेवा विधियों और व्यावसायिक तर्क को मॉडल में संभाला जा सकता है, इसलिए यह एक "समृद्ध" (व्यवहार के साथ) मॉडल होगा।
मैं हमेशा बड़े पैमाने पर दो पुस्तकालयों का उपयोग करता हूं: AFNetworking 2.0 और ReactiveCocoa । मुझे लगता है कि यह किसी भी आधुनिक अनुप्रयोग के लिए होना चाहिए जो नेटवर्क और वेब-सेवाओं के साथ बातचीत करता है या इसमें जटिल यूआई लॉजिक होता है।
आर्किटेक्चर
सबसे पहले मैं एक सामान्य APIClient
वर्ग बनाता हूं , जो AFHTTPSessionManager का उपवर्ग है । यह एप्लिकेशन में सभी नेटवर्किंग का एक वर्कहॉर्स है: सभी सेवा वर्ग इसे वास्तविक REST अनुरोध सौंपते हैं। इसमें HTTP क्लाइंट के सभी अनुकूलन शामिल हैं, जिनकी मुझे विशेष एप्लिकेशन में आवश्यकता है: SSL पिनिंग, त्रुटि प्रसंस्करण और NSError
विस्तृत विफलता के कारणों के साथ सीधी वस्तुएं बनाना और सभी API
और कनेक्शन त्रुटियों का वर्णन (ऐसे मामले में नियंत्रक के लिए सही संदेश दिखा सकेंगे) उपयोगकर्ता), अनुरोध और प्रतिक्रिया क्रमबद्धक, http हेडर और अन्य नेटवर्क से संबंधित सामान सेट करना। तब मैं तार्किक subservices या, और अधिक सही ढंग से, में सभी API अनुरोधों को विभाजित microservices : UserSerivces
, CommonServices
,SecurityServices
,FriendsServices
और इसी तरह, व्यावसायिक तर्क के अनुसार वे लागू होते हैं। इनमें से प्रत्येक माइक्रोसर्विस एक अलग वर्ग है। वे, एक साथ, एक रूप Service Layer
। इन कक्षाओं में प्रत्येक एपीआई अनुरोध, डोमेन मॉडल को संसाधित करने और हमेशा RACSignal
पार्स किए गए प्रतिक्रिया मॉडल या NSError
कॉलर के साथ रिटर्न के तरीके होते हैं ।
मैं यह उल्लेख करना चाहता हूं कि यदि आपके पास जटिल मॉडल क्रमांकन तर्क है - तो इसके लिए एक और परत बनाएं: डेटा मैपर जैसा कुछ लेकिन सामान्य रूप से JSON / XML -> मॉडल मैपर। यदि आपके पास कैश है: तो इसे एक अलग परत / सेवा के रूप में भी बनाएं (आपको कैशिंग के साथ व्यावसायिक तर्क नहीं मिलाना चाहिए)। क्यों? क्योंकि सही कैशिंग परत अपने स्वयं के गोच के साथ काफी जटिल हो सकती है। लोग जटिल तर्क को लागू करने के लिए वैध, पूर्वानुमानित कैशिंग जैसे उदाहरण पेश करते हैं, जैसे कि प्रोफेसरों पर आधारित अनुमानों के साथ मोनोडल कैशिंग। आप अधिक समझने के लिए कार्लोस नामक इस खूबसूरत पुस्तकालय के बारे में पढ़ सकते हैं । और यह मत भूलो कि कोर डेटा वास्तव में सभी कैशिंग मुद्दों के साथ आपकी मदद कर सकता है और आपको कम तर्क लिखने की अनुमति देगा। इसके अलावा, यदि आपके पास रिपॉजिटरी के बीच कुछ तर्क हैंNSManagedObjectContext
मॉडल के और सर्वर अनुरोधों के हैं, तो आप उपयोग कर सकते हैं पैटर्न, जो डेटा को पुनर्प्राप्त करने वाले तर्क को अलग करता है और इसे मॉडल पर कार्य करने वाले व्यावसायिक तर्क से इकाई मॉडल में मैप करता है। इसलिए, मैं कोर डेटा आधारित आर्किटेक्चर होने पर भी रिपॉजिटरी पैटर्न का उपयोग करने की सलाह देता हूं। भंडार कर सकते हैं अमूर्त बातें, जैसे NSFetchRequest
, NSEntityDescription
, NSPredicate
और इतने की तरह सादा तरीकों की पर get
या put
।
सेवा परत में इन सभी कार्यों के बाद, कॉलर (व्यू कंट्रोलर) प्रतिक्रिया के साथ कुछ जटिल अतुल्यकालिक सामान कर सकता है: सिग्नल हेरफेर, चेनिंग, मैपिंग, ReactiveCocoa
आदिम की मदद से , या बस इसे सब्सक्राइब करें और दृश्य में परिणाम दिखाएं। । मैं के साथ इंजेक्षन निर्भरता इंजेक्शन इन सभी सेवा कक्षाएं मेरे में APIClient
है, जो इसी में एक विशेष सेवा कॉल अनुवाद करेगा GET
, POST
, PUT
, DELETE
, आदि REST एंडपॉइंट के लिए अनुरोध। इस मामले APIClient
में सभी नियंत्रकों को स्पष्ट रूप से पारित कर दिया जाता है, आप इसे एक पैरामीरिज्ड ओवर के साथ स्पष्ट कर सकते हैंAPIClient
सेवा वर्गों । यह समझ में आ सकता है कि क्या आप अलग-अलग कस्टमाइज़ेशन का उपयोग करना चाहते हैंAPIClient
विशेष सेवा वर्गों के लिए, लेकिन यदि आप, कुछ कारणों से, अतिरिक्त प्रतियां नहीं चाहते हैं या आप सुनिश्चित हैं कि आप हमेशा एक विशेष उदाहरण (कस्टमाइज़ेशन के बिना) का उपयोग करेंगे APIClient
- तो इसे एक सिंगलटन बनाएं, लेकिन डॉन, कृपया नहीं करें 'टी सर्विस क्लास को सिंगलटन बनाते हैं।
फिर डीआई के साथ प्रत्येक दृश्य नियंत्रक फिर से उस सेवा वर्ग को इंजेक्ट करता है जिसे उसकी आवश्यकता होती है, उपयुक्त सेवा विधियों को कॉल करता है और यूआई लॉजिक के साथ उनके परिणामों की रचना करता है। निर्भरता इंजेक्शन के लिए मैं BloodMagic या एक अधिक शक्तिशाली रूपरेखा टाइफून का उपयोग करना पसंद करता हूं । मैं कभी भी सिंगलटन, गॉड APIManagerWhatever
क्लास या अन्य गलत सामान का इस्तेमाल नहीं करता। क्योंकि यदि आप अपनी कक्षा को बुलाते हैं WhateverManager
, तो यह इंगित करता है कि आप इसका उद्देश्य नहीं जानते हैं और यह एक बुरा डिजाइन विकल्प है । एकल भी एक विरोधी पैटर्न है, और ज्यादातर मामलों में (दुर्लभ लोगों को छोड़कर) एक गलत समाधान है। सिंगलटन को केवल तभी माना जाना चाहिए जब निम्नलिखित तीनों मानदंड संतुष्ट हों:
- एकल उदाहरण का स्वामित्व यथोचित असाइन नहीं किया जा सकता है;
- आलसी आरंभीकरण वांछनीय है;
- अन्यथा के लिए वैश्विक पहुंच प्रदान नहीं की गई है।
हमारे मामले में एकल उदाहरण का स्वामित्व कोई समस्या नहीं है और साथ ही हमें अपने वैश्विक प्रबंधक को सेवाओं में विभाजित करने के बाद वैश्विक पहुँच की आवश्यकता नहीं है, क्योंकि अब केवल एक या कई समर्पित नियंत्रकों को एक विशेष सेवा की आवश्यकता होती है (जैसे UserProfile
नियंत्रक की आवश्यकता UserServices
और इतने पर) ।
हम हमेशा सम्मान करना चाहिए S
में सिद्धांत ठोस और उपयोग चिंताओं की जुदाई , इसलिए, एक कक्षा में सभी आपकी सेवा के तरीकों और नेटवर्क कॉल डाल नहीं है क्योंकि यह पागल है, खासकर यदि आप एक बड़े उद्यम अनुप्रयोग का विकास। इसलिए हमें निर्भरता इंजेक्शन और सेवाओं के दृष्टिकोण पर विचार करना चाहिए। मैं इस दृष्टिकोण को आधुनिक और पोस्ट-ओओ मानता हूं । इस मामले में हम अपने एप्लिकेशन को दो भागों में विभाजित करते हैं: लॉजिक (कंट्रोलर और इवेंट्स) और मापदंडों को नियंत्रित करते हैं।
एक प्रकार के पैरामीटर साधारण "डेटा" पैरामीटर होंगे। यही हम कार्यों के आसपास से गुजरते हैं, हेरफेर करते हैं, संशोधित करते हैं, जारी रखते हैं, आदि ये संस्थाएं, समुच्चय, संग्रह, केस कक्षाएं हैं। दूसरी तरह "पैरामीटर" पैरामीटर होगा। ये वे वर्ग हैं जो व्यावसायिक तर्क को संकुचित करते हैं, बाहरी प्रणालियों के साथ संवाद करने की अनुमति देते हैं, डेटा एक्सेस प्रदान करते हैं।
यहाँ उदाहरण के लिए मेरी वास्तुकला का एक सामान्य वर्कफ़्लो है। मान लें कि हमारे पास एक है FriendsViewController
, जो उपयोगकर्ता के दोस्तों की सूची प्रदर्शित करता है और हमारे पास दोस्तों को हटाने का एक विकल्प है। मैं अपनी FriendsServices
कक्षा में एक विधि बनाता हूँ जिसे:
- (RACSignal *)removeFriend:(Friend * const)friend
जहां Friend
एक मॉडल / डोमेन ऑब्जेक्ट है (या यह केवल एक User
ऑब्जेक्ट हो सकता है यदि उनके पास समान विशेषताएं हैं)। Underhood इस विधि को पार्स Friend
करने के लिए NSDictionary
JSON मापदंडों का friend_id
, name
, surname
, friend_request_id
और इतने पर। मैं हमेशा इस तरह के बॉयलरप्लेट के लिए और अपने मॉडल लेयर के लिए मेंटल लाइब्रेरी का उपयोग करता हूं (आगे और पीछे पार्स करना, JSON में नेस्टेड ऑब्जेक्ट पदानुक्रमों को प्रबंधित करना)। पार्स करने के बाद यह कॉल APIClient
DELETE
एक वास्तविक बाकी अनुरोध और रिटर्न बनाने के लिए विधि Response
में RACSignal
फोन करने वाले (करने के लिए FriendsViewController
हमारे मामले में) उपयोगकर्ता या जो कुछ भी के लिए उचित संदेश प्रदर्शित करने के लिए।
यदि हमारा आवेदन बहुत बड़ा है, तो हमें अपने तर्क को और भी स्पष्ट करना होगा। उदाहरण के लिए, तर्क को एक साथ मिलाना या मॉडल करना हमेशा अच्छा नहीं होता है । जब मैंने अपने दृष्टिकोण का वर्णन किया तो मैंने कहा था कि विधि परत में होनी चाहिए , लेकिन अगर हम अधिक पांडित्यपूर्ण होंगे तो हम नोटिस कर सकते हैं कि यह बेहतर है । आइए याद रखें कि रिपॉजिटरी क्या है। एरिक इवांस ने अपनी पुस्तक [DDD] में इसका सटीक विवरण दिया:Repository
Service
removeFriend
Service
Repository
एक वैचारिक सेट के रूप में एक रिपॉजिटरी एक निश्चित प्रकार की सभी वस्तुओं का प्रतिनिधित्व करता है। यह एक संग्रह की तरह काम करता है, और अधिक विस्तृत क्वेरी क्षमता को छोड़कर।
इसलिए, Repository
अनिवार्य रूप से एक मुखौटा है जो डेटा / वस्तुओं तक पहुंच की आपूर्ति करने के लिए संग्रह शैली शब्दार्थ (ऐड, अपडेट, निकालें) का उपयोग करता है। : कारण है कि जब आप की तरह कुछ है कि getFriendsList
, getUserGroups
, removeFriend
आप इसे में जगह कर सकते हैं Repository
क्योंकि अपने संग्रह की तरह अर्थ विज्ञान सुंदर को यहां साफ़ है। और कोड की तरह:
- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
निश्चित रूप से एक व्यावसायिक तर्क है, क्योंकि यह बुनियादी CRUD
कार्यों से परे है और दो डोमेन ऑब्जेक्ट्स ( Friend
और Request
) को जोड़ता है , इसलिए इसे Service
परत में रखा जाना चाहिए । इसके अलावा, मैं नोटिस करना चाहता हूं: अनावश्यक सार न बनाएं । इन सभी दृष्टिकोणों का बुद्धिमानी से उपयोग करें। क्योंकि यदि आप अपने एप्लिकेशन को सार के साथ अभिभूत कर देंगे, तो इससे इसकी आकस्मिक जटिलता बढ़ जाएगी , और जटिलता कुछ और की तुलना में सॉफ़्टवेयर सिस्टम में अधिक समस्याएं पैदा करती है
मैं आपको एक "पुराने" उद्देश्य-सी उदाहरण का वर्णन करता हूं, लेकिन यह दृष्टिकोण स्विफ्ट भाषा के लिए बहुत अधिक सुधार के साथ अनुकूलित किया जा सकता है, क्योंकि इसमें अधिक उपयोगी विशेषताएं और कार्यात्मक चीनी हैं। मैं अत्यधिक इस पुस्तकालय का उपयोग करने की सलाह देता हूं: मोया । यह आपको एक और अधिक सुंदर APIClient
परत बनाने की अनुमति देता है (हमारा वर्कहॉर्स जैसा कि आपको याद है)। अब हमारा APIClient
प्रदाता प्रोटोकॉल के अनुरूप विस्तार और विनाशकारी पैटर्न मिलान का लाभ उठाने के साथ एक मूल्य प्रकार (एनम) होगा। स्विफ्ट एनम + पैटर्न मिलान हमें क्लासिक कार्यात्मक प्रोग्रामिंग के रूप में बीजीय डेटा प्रकार बनाने की अनुमति देता है । हमारे माइक्रोसर्विसेज इस सुधारे गए APIClient
प्रदाता का उपयोग सामान्य उद्देश्य-सी दृष्टिकोण के रूप में करेंगे। Mantle
आप के बजाय मॉडल परत के लिए ObjectMapper पुस्तकालय का उपयोग कर सकते हैंया मुझे अधिक सुरुचिपूर्ण और कार्यात्मक अर्गो लाइब्रेरी का उपयोग करना पसंद है ।
इसलिए, मैंने अपने सामान्य वास्तुशिल्प दृष्टिकोण का वर्णन किया, जिसे किसी भी आवेदन के लिए अनुकूलित किया जा सकता है, मुझे लगता है। इसमें और भी बहुत कुछ सुधार हो सकते हैं। मैं आपको कार्यात्मक प्रोग्रामिंग सीखने की सलाह देता हूं, क्योंकि आप इससे बहुत लाभ उठा सकते हैं, लेकिन इसके साथ बहुत दूर न जाएं। अत्यधिक, साझा, वैश्विक परिवर्तनशील राज्य को समाप्त करने, एक अपरिवर्तनीय डोमेन मॉडल बनाने या बाहरी दुष्प्रभावों के बिना शुद्ध कार्य बनाने के लिए, आम तौर पर, एक अच्छा अभ्यास है, और नई Swift
भाषा इसे प्रोत्साहित करती है। लेकिन हमेशा याद रखें, भारी शुद्ध कार्यात्मक पैटर्न के साथ अपने कोड को ओवरलोड करना, श्रेणी-सैद्धांतिक दृष्टिकोण एक बुरा विचार है, क्योंकि आपके अपरिवर्तनीय मॉडल में अन्य डेवलपर्स आपके कोड को पढ़ेंगे और समर्थन करेंगे, और वे निराश या डरावना हो सकते हैंprismatic profunctors
और इस तरह के सामान। के साथ एक ही बात ReactiveCocoa
: RACify
अपने कोड नहीं है बहुत ज्यादा है , क्योंकि यह वास्तव में तेजी से अपठनीय बन सकता है, विशेष रूप से newbies के लिए। इसका उपयोग तब करें जब यह वास्तव में आपके लक्ष्यों और तर्क को सरल बना सके।
तो, read a lot, mix, experiment, and try to pick up the best from different architectural approaches
। यह सबसे अच्छी सलाह है जो मैं आपको दे सकता हूं।