इकाई फ्रेमवर्क इकाई - वेब सेवा से कुछ डेटा - सर्वश्रेष्ठ वास्तुकला?


10

वर्तमान में हम कुछ वेब अनुप्रयोगों में ORM के रूप में Entity फ्रेमवर्क का उपयोग कर रहे हैं, और अब तक, इसने हमें अच्छी तरह से अनुकूल किया है क्योंकि हमारे सभी डेटा एक ही डेटाबेस में संग्रहीत हैं। हम रिपॉजिटरी पैटर्न का उपयोग कर रहे हैं, और इसमें सेवाएँ (डोमेन लेयर) हैं जो इनका उपयोग करते हैं, और EF संस्थाओं को सीधे ASP.NET MVC नियंत्रकों को लौटाते हैं।

हालाँकि, एक 3 पार्टी एपीआई (एक वेब सेवा के माध्यम से) का उपयोग करने के लिए एक आवश्यकता आ गई है जो हमें अतिरिक्त जानकारी देगा जो हमारे डेटाबेस में उपयोगकर्ता से संबंधित है। हमारे स्थानीय उपयोगकर्ता डेटाबेस में, हम एक बाहरी आईडी स्टोर करेंगे जिसे हम अतिरिक्त जानकारी प्राप्त करने के लिए एपीआई प्रदान कर सकते हैं। काफी जानकारी उपलब्ध है, लेकिन सादगी के लिए, उनमें से एक उपयोगकर्ता की कंपनी (नाम, प्रबंधक, कमरा, नौकरी का शीर्षक, स्थान आदि) से संबंधित है। इस जानकारी का उपयोग हमारे वेब एप्लिकेशन में विभिन्न स्थानों पर किया जाएगा - जैसा कि एक ही स्थान पर उपयोग किए जाने के विपरीत है।

तो मेरा सवाल यह है कि इस जानकारी को पॉप्युलेट और एक्सेस करने के लिए सबसे अच्छी जगह कहाँ है? चूंकि इसका उपयोग विभिन्न स्थानों पर किया जाता है, इसलिए वेब अनुप्रयोग में हम जहां भी इसका उपयोग करते हैं, वहां इसे तदर्थ आधार पर लाना वास्तव में समझदारी नहीं है - इसलिए यह इस अतिरिक्त डेटा को डोमेन परत से वापस करने के लिए समझ में आता है।

मेरा प्रारंभिक विचार सिर्फ एक रैपर मॉडल वर्ग बनाना था जिसमें EF निकाय (EFUser) शामिल होगा, और एक नया 'ApiUser' वर्ग जिसमें नई जानकारी होगी - और जब हमें कोई उपयोगकर्ता मिलेगा, तो हमें EFUser मिलेगा, और फिर अतिरिक्त मिलेगा एपीआई से जानकारी, और ApiUser वस्तु आबाद। हालाँकि, एकल उपयोगकर्ताओं को प्राप्त करने के लिए ठीक होने के दौरान, यह कई उपयोगकर्ताओं को प्राप्त होने पर गिर जाता है। उपयोगकर्ताओं की सूची प्राप्त करते समय हम एपीआई को हिट नहीं कर सकते।

मेरा दूसरा विचार सिर्फ EFUser इकाई में एक सिंगलटन विधि जोड़ना था जो ApiUser को लौटाता है, और जरूरत पड़ने पर बस पॉप्युलेट करता है। यह उपरोक्त समस्या को हल करता है क्योंकि हम इसे केवल तब उपयोग करते हैं जब हमें इसकी आवश्यकता होती है।

या अंतिम विचार यह था कि हमारे डेटाबेस में डेटा की एक स्थानीय प्रतिलिपि रखें, और उपयोगकर्ता द्वारा लॉग इन करने पर इसे एपीआई के साथ सिंक्रनाइज़ करें। यह न्यूनतम काम है क्योंकि यह सिर्फ एक सिंक्रनाइज़ेशन प्रक्रिया है - और हमारे पास हिटिंग का ओवरहेड नहीं है। हर बार उपयोगकर्ता जानकारी प्राप्त करने के लिए DB और API। हालाँकि, इसका अर्थ है कि डेटा को दो स्थानों पर संग्रहीत करना, और यह भी मतलब है कि डेटा किसी भी उपयोगकर्ता के लिए पुराना नहीं है, जो कुछ समय के लिए लॉग इन नहीं हुआ है।

क्या किसी के पास इस तरह के परिदृश्य को संभालने के लिए कोई सलाह या सुझाव है?


it's not really sensible to fetch it on an ad-hoc basis-- क्यों? प्रदर्शन कारणों से?
रॉबर्ट हार्वे

मेरा मतलब है कि एड-हॉक आधार पर एपीआई को मारना नहीं है - मेरा मतलब केवल मौजूदा इकाई संरचना को बनाए रखना है, और फिर जरूरत पड़ने पर वेब एप्लिकेशन में एपीआई एड-हॉक को कॉल करना - मेरा मतलब सिर्फ यही नहीं होगा। समझदार के रूप में यह बहुत से स्थानों में किया जाना चाहिए।
स्टेवहाइटर

जवाबों:


3

आपका मुकदमा

आपके मामले में सभी तीन विकल्प व्यवहार्य हैं। मुझे लगता है कि सबसे अच्छा विकल्प संभवत: आपके डेटा स्रोतों को सिंक करने के लिए है। asp.net एप्लिकेशन का भी पता नहीं है। यही है, हर बार अग्रभूमि में दो भ्रूणों से बचें, एपीआई को डीबी के साथ चुपचाप सिंक करें)। इसलिए अगर आपके मामले में यह एक व्यवहार्य विकल्प है - मैं कहता हूं कि यह करो।

एक समाधान जहां आप दूसरे उत्तर की तरह 'एक बार' लाने का सुझाव देते हैं, यह बहुत व्यवहार्य प्रतीत नहीं होता है क्योंकि यह प्रतिक्रिया को कहीं भी जारी नहीं रखता है और ASP.NET MVC बस हर अनुरोध पर और अधिक के लिए लाने की कोशिश करेगा।

मैं सिंगलटन से बचूंगा, मुझे नहीं लगता कि यह सामान्य कारणों में से एक है।

यदि तीसरा विकल्प व्यवहार्य नहीं है - एक विकल्प आलसी को लोड करना है। यही है, एक वर्ग को इकाई का विस्तार करना है, और क्या इसने आधार की आवश्यकता पर एपीआई को मारा है । यह एक बहुत ही खतरनाक अमूर्त है, क्योंकि यह और भी अधिक जादू और गैर-स्पष्ट स्थिति है।

मुझे लगता है कि यह वास्तव में कई सवालों से उब गया है:

  • एपीआई कॉल डेटा कितनी बार बदलता है? प्रायः नहीं? तीसरा विकल्प। अक्सर? अचानक तीसरा विकल्प भी व्यवहार्य नहीं है। मुझे यकीन नहीं है कि मैं आप के रूप में तदर्थ कॉल के खिलाफ हूँ।
  • एपीआई कॉल कितना महंगा है? क्या आप प्रति कॉल का भुगतान करते हैं? क्या वे उपवास करते हैं? नि: शुल्क? यदि वे तेज़ हैं, तो हर बार कॉल करना काम कर सकता है, यदि वे धीमे हों तो आपको किसी प्रकार की भविष्यवाणी करने की आवश्यकता होती है और कॉल करें। यदि वे पैसे खर्च करते हैं - यह कैशिंग के लिए एक बड़ा प्रोत्साहन है।
  • प्रतिक्रिया समय कितना तेज़ होना चाहिए? स्पष्ट रूप से तेजी से बेहतर है, लेकिन सादगी के लिए बलिदान करना कुछ मामलों में इसके लायक हो सकता है, खासकर अगर यह सीधे उपयोगकर्ता का सामना नहीं कर रहा है।
  • API डेटा आपके डेटा से कितना अलग है? क्या वे दो वैचारिक रूप से भिन्न चीजें हैं? यदि ऐसा है, तो बेहतर हो सकता है कि एपीआई परिणाम को सीधे वापस करने के बजाय केवल बाहर एपीआई को उजागर करें और दूसरे पक्ष को दूसरी कॉल करें और इसे प्रबंधित करने दें।

चिंताओं को अलग करने के बारे में एक शब्द या दो

बोबसन यहां चिंताओं से अलग होने के बारे में क्या कह रहे हैं, इसके खिलाफ मुझे बहस करने की अनुमति दें। दिन के अंत में - उस तर्क को उस तरह की संस्थाओं में रखना जो चिंताओं के अलगाव का उल्लंघन करता है।

इस तरह का भंडार होने से व्यावसायिक तर्क परत में प्रस्तुति केंद्रित तर्क डालकर चिंताओं को अलग करने का उल्लंघन होता है। आपकी रिपॉजिटरी अब प्रस्तुति संबंधित चीजों से अचानक अवगत है जैसे कि आप उपयोगकर्ता को अपने asp.net mvc नियंत्रकों में कैसे प्रदर्शित करते हैं।

में इस संबंधित सवाल मैं एक नियंत्रक से सीधे गई चीजों का उपयोग के बारे में पूछा है। मुझे वहां से किसी एक उत्तर को उद्धृत करने की अनुमति दें:

"आपका स्वागत है BigPizza, कस्टम पिज़्ज़ा शॉप, क्या मैं आपका ऑर्डर ले सकता हूं?" "ठीक है, मैं जैतून के साथ पिज्जा लेना चाहूंगा, लेकिन सबसे ऊपर टमाटर सॉस और तल पर पनीर और 90 मिनट के लिए ओवन में सेंकना जब तक यह ग्रेनाइट की एक सपाट चट्टान की तरह काला और कठोर न हो।" "ठीक है, सर, कस्टम पिज़ा हमारा पेशा है, हम इसे बनाएंगे।"

खजांची रसोई में जाता है। "काउंटर पर एक साइको है, वह पिज्जा के साथ चाहता है ... यह ग्रेनाइट की एक चट्टान है ... प्रतीक्षा करें ... हमें पहले एक नाम रखने की आवश्यकता है", वह रसोइया को बताता है।

"नहीं!", रसोइया चिल्लाता है, "फिर से नहीं! आप जानते हैं कि हमने पहले ही कोशिश की थी।" वह 400 पृष्ठों के साथ कागज का एक ढेर लेता है, "यहां हमारे पास 2005 से ग्रेनाइट की चट्टान है, लेकिन ... इसमें जैतून नहीं था, लेकिन इसके बजाय पैपरिका ... या यहां शीर्ष टमाटर है ... लेकिन ग्राहक इसे चाहते थे। केवल आधा मिनट बेक किया गया। " "शायद हम इसे TopTomatoGraniteRockSpecial कह सकते हैं?" "लेकिन यह पनीर को ध्यान में नहीं रखता है ..." खजांची: "यही विशेष व्यक्त करने वाला है।" "लेकिन पिज्जा रॉक एक पिरामिड की तरह गठित होने के साथ ही विशेष होगा", कुक जवाब देता है। "हम्म्म ... यह मुश्किल है ...", हताश खजांची कहते हैं।

"मेरे पिज्जा में ऑलियाडी है?", अचानक वह रसोई के दरवाजे से चिल्लाती है। "चलो इस चर्चा को रोकते हैं, बस मुझे बताएं कि इस पिज्जा को कैसे बनाया जाए, हम दूसरी बार ऐसा पिज्जा नहीं बना रहे हैं", रसोइयों ने कहा। "ठीक है, यह जैतून के साथ एक पिज्जा है, लेकिन शीर्ष पर टमाटर सॉस और तल पर पनीर और 90 मिनट के लिए ओवन में सेंकना जब तक कि यह ग्रेनाइट की एक सपाट चट्टान की तरह काला और कठोर न हो।"

(शेष उत्तर पढ़ें, यह वास्तव में अच्छा है)।

यह इस तथ्य को अनदेखा करने के लिए भोली है कि एक डेटाबेस है - एक डेटाबेस है, और इससे कोई फर्क नहीं पड़ता कि आप कितना सार निकालना चाहते हैं, यह कहीं भी नहीं जा रहा है। आपका एप्लिकेशन डेटा स्रोत से अवगत होगा । आप इसे 'हॉट स्वैप' नहीं कर पाएंगे। ORM उपयोगी होते हैं लेकिन वे इस वजह से लीक होते हैं कि वे कितनी जटिल समस्या को हल करते हैं और प्रदर्शन के बहुत सारे कारणों के लिए (जैसे उदाहरण के लिए n + 1 का चयन करें)।


आपकी बहुत अच्छी प्रतिक्रिया @Benjamin के लिए धन्यवाद। मैंने शुरू में बोब्सन के समाधान का उपयोग करके एक प्रोटोटाइप बनाना शुरू कर दिया था (इससे पहले कि वह अपना उत्तर पोस्ट करे), लेकिन आप कुछ महत्वपूर्ण बिंदुओं को उठाते हैं। आपके प्रश्नों का उत्तर देने के लिए,: - एपीआई कॉल बहुत महंगी नहीं है (वे मुफ़्त हैं, और तेज़ भी हैं)। - डेटा के कुछ हिस्से नियमित रूप से (कुछ घंटों में भी कुछ) काफी बदल जाएंगे। - गति काफी महत्वपूर्ण है, लेकिन एप्लिकेशन के दर्शक ऐसे हैं कि तेज़ लोडिंग को हल्का करना एक पूर्ण आवश्यकता नहीं है।
स्टेवहाइटर

@stevehayter उस स्थिति में, मैं सबसे अधिक संभावना ग्राहक की ओर से एपीआई को कॉल करने के लिए होगा। यह सस्ता और तेज है, और यह आपको महीन दानेदार नियंत्रण देता है।
बेंजामिन ग्रुएनबाम

1
इन उत्तरों के आधार पर, मैं डेटा की एक स्थानीय प्रति रखने के लिए कम झुक रहा हूं। मैं वास्तव में एपीआई को अलग से उजागर करने की ओर झुक रहा हूं, और इस तरह से अतिरिक्त डेटा को संभाल रहा हूं। मुझे लगता है कि यह @ बोब्सन के समाधान की सादगी के बीच एक अच्छा समझौता हो सकता है, लेकिन इसमें अलगाव की एक डिग्री भी शामिल है, जिसके बारे में मैं थोड़ा अधिक सहज हूं। मैं इस रणनीति को अपने प्रोटोटाइप में देखूंगा, और अपने निष्कर्षों के साथ वापस रिपोर्ट करूंगा (और इनाम को पुरस्कार दूंगा!)।
स्टीवेहाईटर

@BenjaminGruenbaum - मुझे यकीन नहीं है कि मैं आपके तर्क का पालन करूँगा। मेरा सुझाव रिपॉजिटरी को प्रस्तुति के बारे में कैसे बताता है? निश्चित रूप से, यह ज्ञात है कि एक एपीआई-समर्थित फ़ील्ड तक पहुँचा गया है, लेकिन इसका उस जानकारी के साथ क्या करना है, इससे कोई लेना-देना नहीं है।
बोबसन

1
मैंने क्लाइंट-साइड में सब कुछ स्थानांतरित करने का विकल्प चुना - लेकिन EFUser पर एक विस्तार विधि के रूप में (जो प्रस्तुति परत में मौजूद है, एक अलग विधानसभा में)। विधि केवल एपीआई से डेटा लौटाती है, और एक सिंगलटन सेट करती है ताकि यह बार-बार हिट न हो। वस्तुओं का जीवनकाल इतना कम है कि मुझे यहां एक सिंगलटन का उपयोग करने में कोई समस्या नहीं है। इस तरह कुछ हद तक अलग हो जाता है, लेकिन मुझे अभी भी EFUser इकाई के साथ काम करने की सुविधा प्राप्त है। सभी उत्तरदाताओं को उनकी मदद के लिए धन्यवाद। निश्चित रूप से एक दिलचस्प चर्चा :)।
स्टेवहाइटर

2

चिंताओं के उचित पृथक्करण के साथ , इकाई ढांचे / एपीआई स्तर से ऊपर कुछ भी महसूस नहीं करना चाहिए कि डेटा कहां से आ रहा है। जब तक एपीआई कॉल महंगी नहीं होती (समय या प्रसंस्करण के संदर्भ में), इसका उपयोग करने वाले डेटा तक डेटाबेस से डेटा एक्सेस करना उतना ही पारदर्शी होना चाहिए।

इसे लागू करने का सबसे अच्छा तरीका, फिर उस EFUserवस्तु में अतिरिक्त गुण जोड़ना होगा, जो आवश्यकतानुसार एपीआई डेटा को आलसी-लोड करता है। कुछ इस तरह:

partial class EFUser
{
    private APIUser _apiUser;
    private APIUser ApiUser
    {
       get { 
          if (_apiUser == null) _apiUser = API.LoadUser(this.ExternalID);
          return _apiUser;
       }
    }
    public string CompanyName { get { return ApiUser.UserCompanyName; } }
    public string JobTitle{ get { return ApiUser.UserJobTitle; } }
}

बाह्य, पहली बार या तो CompanyNameया JobTitleप्रयोग किया जाता है वहाँ एक भी एपीआई फोन किया (और इस प्रकार एक छोटे से देरी), लेकिन बाद में सभी कॉल्स हो जब तक वस्तु नष्ट हो जाता है बस के रूप में जल्दी और आसानी से डेटाबेस का उपयोग के रूप में होगा।


धन्यवाद @Bobson ... यह वास्तव में प्रारंभिक मार्ग था जिसे मैंने नीचे जाना शुरू कर दिया (कुछ विस्तार विधियों के साथ उपयोगकर्ताओं की सूची के विवरणों को लोड करने के लिए जोड़ा गया - उदाहरण के लिए, उपयोगकर्ताओं की सूची के लिए कंपनी का नाम प्रदर्शित करना)। ऐसा लगता है कि अब तक मेरी जरूरतों के अनुरूप है - लेकिन बेंजामिन नीचे कुछ महत्वपूर्ण बिंदुओं को उठाता है, इसलिए मैं इस सप्ताह का मूल्यांकन जारी रखने जा रहा हूं।
स्टीववेंटर

0

एक विचार यह है कि ApiUser को संशोधित करने के लिए हमेशा अतिरिक्त जानकारी न हो। इसके बजाय, आप इसे लाने के लिए ApiUser पर एक विधि रखते हैं:

ApiUser apiUser = backend.load($id);
//Locally available data directly on the ApiUser like so:
String name = apiUser.getName();
//Expensive extra data available after extra call:
UserExtraData extraData = apiUser.fetchExtraData();
String managerName = extraData.getManagerName();

आप अतिरिक्त डेटा के आलसी लोडिंग का उपयोग करने के लिए इसे थोड़ा संशोधित कर सकते हैं, ताकि आपको ApiUser ऑब्जेक्ट से UserExtraData को निकालना न पड़े:

//Extra data fetched on first get:
String managerName = apiUser.lazyGetExtraData().getManagerName();

इस तरह, जब आपके पास एक सूची होगी, तो अतिरिक्त डेटा डिफ़ॉल्ट रूप से नहीं मिलेगा। लेकिन आप सूची को ट्रेस करते हुए भी इसे एक्सेस कर सकते हैं!


वास्तव में यह निश्चित नहीं है कि आपका यहाँ क्या अर्थ है - backend.load () में, हम पहले से ही एक लोड कर रहे हैं - इतना निश्चित रूप से कि एपीआई डेटा लोड होगा?
स्टीवेहाईटर

मेरा मतलब है कि आपको अतिरिक्त लोड का इंतजार तब तक करना चाहिए जब तक कि स्पष्ट रूप से अनुरोध नहीं किया जाता है - आलसी एप डेटा को लोड करता है।
अलेक्जेंडर टॉर्टलिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.