2012-07-18 6 views
22

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

एक स्लिम (ए.के.ए. स्कैलाक्वायर) test example से, इस तरह आप कई से अधिक डेटाबेस संबंध बनाते हैं। यह 3 टेबल बनाएगा: ए, बी और a_to_b, जहां a_to_b तालिका ए और बी में पंक्तियों के लिंक रखता है।

object A extends Table[(Int, String)]("a") { 
    def id = column[Int]("id", O.PrimaryKey) 
    def s = column[String]("s") 
    def * = id ~ s 
    def bs = AToB.filter(_.aId === id).flatMap(_.bFK) 
} 

object B extends Table[(Int, String)]("b") { 
    def id = column[Int]("id", O.PrimaryKey) 
    def s = column[String]("s") 
    def * = id ~ s 
    def as = AToB.filter(_.bId === id).flatMap(_.aFK) 
} 

object AToB extends Table[(Int, Int)]("a_to_b") { 
    def aId = column[Int]("a") 
    def bId = column[Int]("b") 
    def * = aId ~ bId 
    def aFK = foreignKey("a_fk", aId, A)(a => a.id) 
    def bFK = foreignKey("b_fk", bId, B)(b => b.id) 
} 

(A.ddl ++ B.ddl ++ AToB.ddl).create 
A.insertAll(1 -> "a", 2 -> "b", 3 -> "c") 
B.insertAll(1 -> "x", 2 -> "y", 3 -> "z") 
AToB.insertAll(1 -> 1, 1 -> 2, 2 -> 2, 2 -> 3) 

val q1 = for { 
    a <- A if a.id >= 2 
    b <- a.bs 
} yield (a.s, b.s) 
q1.foreach(x => println(" "+x)) 
assertEquals(Set(("b","y"), ("b","z")), q1.list.toSet) 

मेरी अगले कदम के रूप में, मैं इस ऊपर वस्तुओं के साथ काम करने के लिए, एक स्तर (मैं अभी भी चालाक का उपयोग करें, लेकिन यह अच्छी तरह से लपेट करना चाहते हैं) लेना चाहते हैं। तो छद्म कोड में कुछ ऐसा करना अच्छा होगा:

objectOfTypeA.save() 
objectOfTypeB.save() 
linkAtoB.save(ojectOfTypeA, objectOfTypeB) 

या ऐसा कुछ। मेरे पास जावा पर इस तरह से विचार करने के बारे में मेरे विचार हैं, लेकिन मुझे एहसास हो रहा है कि शुद्ध ओओ भाषाओं से मेरे कुछ ऑब्जेक्ट उन्मुख विचारों में मुझे असफल होना शुरू हो रहा है। क्या कोई मुझे कृपया कुछ पॉइंटर्स दे सकता है कि स्कैला में इस समस्या से कैसे संपर्क करें।

उदाहरण के लिए: क्या मैं साधारण ऑब्जेक्ट्स बनाता हूं जो टेबल ऑब्जेक्ट को लपेट या विस्तारित करते हैं, और फिर इन (रचना) को किसी अन्य वर्ग में शामिल करते हैं जो उन्हें प्रबंधित करता है?

कोई विचार, मार्गदर्शन, उदाहरण (कृपया), जो मुझे इस समस्या से बेहतर तरीके से संपर्क करने में मदद करेगा क्योंकि एक डिजाइनर और कोडर की सराहना की जाएगी।

+14

दस हजार स्केल सवाल! –

+1

@oxbow_lakes, और उस पर एक बहुत अच्छा! – missingfaktor

+0

@oxbow_lakes यह 5 साल पहले था। 100k वें स्कैला प्रश्न की प्रतीक्षा –

उत्तर

5

सरल दृढ़ता आवश्यकताओं के लिए एक अच्छा समाधान ActiveRecord पैटर्न है: http://en.wikipedia.org/wiki/Active_record_pattern। यह रूबी और प्ले में लागू किया गया है! फ्रेमवर्क 1.2, और आप आसानी से स्केल में इसे स्टैंड-अलोन एप्लिकेशन

एकमात्र आवश्यकता है कि आपको आवश्यक डीबी का संदर्भ प्राप्त करने के लिए सिंगलटन डीबी या सिंगलटन सेवा हो।

  • एक सामान्य विशेषता ActiveRecord
  • एक सामान्य typeclass ActiveRecordHandler

implicits की शक्ति का शोषण, तो आप एक अद्भुत वाक्य रचना प्राप्त कर सकते हैं:

मैं व्यक्तिगत रूप से एक कार्यान्वयन निम्नलिखित के आधार पर के लिए जाना होगा
trait ActiveRecordHandler[T]{ 

    def save(t:T):T 

    def delete[A<:Serializable](primaryKey:A):Option[T] 

    def find(query:String):Traversable[T] 
} 

object ActiveRecordHandler { 
    // Note that an implicit val inside an object with the same name as the trait 
    // is one of the way to have the implicit in scope. 
    implicit val myClassHandler = new ActiveRecordHandler[MyClass] { 

    def save(myClass:MyClass) = myClass 

    def delete[A <: Serializable](primaryKey: A) = None 

    def find(query: String) = List(MyClass("hello"),MyClass("goodbye")) 
    } 
} 

trait ActiveRecord[RecordType] { 
    self:RecordType=> 


    def save(implicit activeRecordHandler:ActiveRecordHandler[RecordType]):RecordType = activeRecordHandler.save(this) 

    def delete[A<:Serializable](primaryKey:A)(implicit activeRecordHandler:ActiveRecordHandler[RecordType]):Option[RecordType] = activeRecordHandler.delete(primaryKey) 
} 

case class MyClass(name:String) extends ActiveRecord[MyClass] 

object MyClass { 
    def main(args:Array[String]) = { 
    MyClass("10").save 
    } 
} 

ऐसे समाधान के साथ, आपको केवल अपनी कक्षा की आवश्यकता ActiveRecord [T] तक बढ़ाने की आवश्यकता है और टी को संभालने के लिए एक अंतर्निहित ActiveRecordHandler [T] है उनके।

वास्तव में एक कार्यान्वयन भी है: https://github.com/aselab/scala-activerecord जो समान विचार पर आधारित है, लेकिन ActiveRecord को एक सार प्रकार बनाने के बजाय, यह एक सामान्य साथी वस्तु घोषित करता है।

ActiveRecord पैटर्न पर

एक सामान्य लेकिन बहुत महत्वपूर्ण टिप्पणी यह ​​दृढ़ता के मामले में सरल आवश्यकताओं को पूरा करने में मदद करता है, लेकिन और अधिक जटिल आवश्यकताओं के साथ सौदा नहीं कर सकते हैं: उदाहरण के लिए, जब आप एक ही के तहत एक से अधिक ऑब्जेक्ट बनाए रखना चाहते लेन-देन।

अपने आवेदन और अधिक जटिल हठ तर्क की आवश्यकता है, सबसे अच्छा तरीका एक हठ सेवा है जो केवल ग्राहक वर्गों के लिए काम करता है के एक सीमित सेट को उजागर करता है, उदाहरण के लिए लागू करने के लिए है

def persist(objectsofTypeA:Traversable[A],objectsOfTypeB:Traversable[B])

कृपया ध्यान दें कि आपके आवेदन जटिलता के अनुसार, आप विभिन्न फैशन में इस तर्क को बेनकाब करने के लिए चाहते हो सकता:

    मामले आपके आवेदन सरल है में एक सिंगलटन वस्तु के रूप में
  • , और आप अपने persiste नहीं करना चाहते एनसी तर्क प्लग करने योग्य
  • एक सिंगलटन ऑब्जेक्ट के माध्यम से जो "अनुप्रयोग संदर्भ" के रूप में कार्य करता है, ताकि स्टार्टअप पर आपके एप्लिकेशन में आप तय कर सकें कि आप किस दृढ़ता तर्क का उपयोग करना चाहते हैं।
  • कुछ प्रकार के लुकअप सेवा पैटर्न के साथ, यदि आपका एप्लिकेशन वितरित किया गया है।
+0

सुंदर उदाहरण और महान स्पष्टीकरण के लिए धन्यवाद। हो सकता है कि मैं गलतफहमी कर रहा हूं, लेकिन मैं ऊपर दिए गए मेरे उदाहरण का विकल्प खोजने की कोशिश नहीं कर रहा हूं, बल्कि मैं अपनी व्यावसायिक चिंताओं से इसे अलग करने के लिए एक डिज़ाइन की तलाश कर रहा हूं (SLICK/ScalaQuery)। आपका उदाहरण बेहद उपयोगी है, लेकिन क्या आप इसे प्रदान किए गए स्निपेट के "वैकल्पिक-से" या "परत-पर-शीर्ष" के रूप में सुझाव दे रहे हैं? – Jack

+0

यह आपको डोमेनऑब्जेक्ट.save() करने देता है। यह दृढ़ता को संभालने के लिए सबसे आम पैटर्न में से एक है: प्रत्येक वस्तु प्रत्येक अपनी दृढ़ता तंत्र के लिए ज़िम्मेदार है। दूसरे भाग में मैं आपको एक अलग पैटर्न सुझाता हूं, यानी दृढ़ता की एक केंद्रीकृत परत (कुछ अर्थ में जे 2 ईई में डीएओ पैटर्न के करीब) – Edmondo1984

+0

@ एडमंडो 1 9 84, कृपया [ग्लोबल स्टेट एंड सिंगलेट्स] (http://www.youtube देखें) .com/watch? v = -FRm3VPhseI) व्याख्यान। –

6

सबसे अच्छा विचार data mapper पैटर्न जैसे कुछ को लागू करना होगा। जो, सक्रिय रिकॉर्ड के विपरीत, एसआरपी का उल्लंघन नहीं करेगा।

चूंकि मैं स्कैला डेवलपर नहीं हूं, इसलिए मैं कोई कोड नहीं दिखाऊंगा।

विचार पीछा कर रहा है:

  • तत्व पर
  • सेट की स्थिति डोमेन वस्तु उदाहरण बनाना चाहेंगे (उदाहरण setId(42) के लिए, आप आईडी के आधार पर तत्व की तलाश कर रहे हैं)
  • बनाने डेटा नक्शाकार उदाहरण
  • डोमेन ऑब्जेक्ट में पैरामीटर
के रूप में डोमेन ऑब्जेक्ट में गुजरकर मैपर पर fetch() विधि निष्पादित करें 210

मैपर प्रदान किए गए डोमेन ऑब्जेक्ट के वर्तमान पैरामीटर को देखेगा और, उन पैरामीटर के आधार पर, स्टोरेज से जानकारी पुनर्प्राप्त करेगा (जो SQL डेटाबेस हो सकता है, या JSON फ़ाइल हो सकता है या शायद रिमोट आरईएसटी एपीआई)। यदि जानकारी पुनर्प्राप्त की जाती है, तो यह मान को डोमेन ऑब्जेक्ट को असाइन करती है।

इसके अलावा, मुझे ध्यान रखना चाहिए कि डेटा मैपर विशिष्ट डोमेन ऑब्जेक्ट के इंटरफ़ेस के साथ काम के लिए बनाए जाते हैं, लेकिन डोमेन ऑब्जेक्ट से स्टोरेज और बैक में जो जानकारी वे पास करते हैं, उन्हें कई SQL टेबल या एकाधिक REST संसाधनों में मैप किया जा सकता है।

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

डोमेन ऑब्जेक्ट (या, कुछ मामलों में, डोमेन ऑब्जेक्ट्स का संग्रह) पूरी तरह से अनजान होगा कि यह संग्रहीत या पुनर्प्राप्त किया गया है या नहीं। डेटा मैपर की ज़िम्मेदारी होगी।

यदि यह सब एमवीसी संदर्भ में है, तो, इसे पूरी तरह कार्यान्वित करने के लिए, आपको मॉडल परत में संरचनाओं के एक और समूह की आवश्यकता होगी। मैं उन्हें "सेवाएं" कहता हूं (कृपया साझा करें, आप बेहतर नाम के साथ आते हैं)। वे डेटा मैपर और डोमेन ऑब्जेक्ट्स के बीच बातचीत को शामिल करने के लिए ज़िम्मेदार हैं। इस तरह आप प्रेजेंटेशन लेयर (नियंत्रक, सटीक होने) में लीक करने से व्यवसाय तर्क को रोक सकते हैं, और ये सेवाएं व्यवसाय के बीच बातचीत (मॉडल के रूप में भी जानी जाती हैं) परत और प्रस्तुति परत के लिए एक प्राकृतिक इंटरफ़ेस बनाती हैं।

पीएस एक बार फिर, खेद है कि मैं कोई कोड उदाहरण प्रदान नहीं कर सकता, क्योंकि मैं एक PHP डेवलपर हूं और स्कैला में कोड लिखना नहीं है।

पी.पी.एस. यदि आप डेटा मैपर पैटर्न का उपयोग कर रहे हैं, तो सबसे अच्छा विकल्प मैन्युअल रूप से मैपर्स लिखना है और किसी भी तृतीय पक्ष ओआरएम का उपयोग नहीं करना है, जो इसे लागू करने का दावा करता है। यह आपको कोडबेस पर अधिक नियंत्रण देगा और व्यर्थ तकनीकी ऋण[1][2] से बचें।

+0

उत्तर के लिए धन्यवाद। मैं वास्तव में सराहना करता हूं कि आपने स्काला प्रश्न में मदद करने के लिए समय निकाला है, भले ही यह आपकी भाषा न हो। फिर भी, मुझे इसे स्कैला से जोड़ने की ज़रूरत है, क्योंकि मैं विशिष्ट समस्याओं के लिए अच्छे स्केल समाधानों की अपनी समझ में सुधार करने की कोशिश कर रहा हूं, खासकर जब स्कैला एक कार्यात्मक प्रोग्रामिंग भाषा है, और मैं एक अनिवार्य प्रोग्रामिंग पृष्ठभूमि से आया हूं। – Jack

+0

विचार [आईओसी] (http://martinfowler.com/articles/injection.html), [LoD] (http://c2.com/cgi/wiki?LawOfDemeter) और [SOC] (http: // en .wikipedia.org/विकी/Separation_of_concerns) किसी भाषा से बंधे नहीं हैं। यदि आप भाषा जानते हैं, तो अवधारणा को लागू करना सरल होना चाहिए। दूसरी तरफ, यदि आप अभी भी भाषा सीख रहे हैं (लेकिन सामान्य रूप से प्रोग्रामिंग को समझते हैं), तो आपको गैर-छोटी समस्याओं के समाधान से शुरू नहीं करना चाहिए। –