दृढ़ता से निपटने के लिए कमांड हैंडलर का उपयोग करने का पैटर्न कैसे शुद्ध रूप से कार्यात्मक भाषा में फिट होता है, जहां हम संभव के रूप में पतली से IO-संबंधित कोड बनाना चाहते हैं?
डोमेन-डिज़ाइन डिज़ाइन को ऑब्जेक्ट-ओरिएंटेड भाषा में लागू करते समय, राज्य परिवर्तनों को निष्पादित करने के लिए कमांड / हैंडलर पैटर्न का उपयोग करना आम है । इस डिज़ाइन में, कमांड हैंडलर आपके डोमेन ऑब्जेक्ट्स के शीर्ष पर बैठते हैं, और रिपॉजिटरी का उपयोग करने और डोमेन इवेंट्स को प्रकाशित करने जैसे उबाऊ दृढ़ता से संबंधित तर्क के लिए जिम्मेदार हैं। हैंडलर आपके डोमेन मॉडल का सार्वजनिक चेहरा हैं; यूआई जैसे एप्लिकेशन कोड हैंडलर को तब बुलाते हैं जब उसे डोमेन ऑब्जेक्ट की स्थिति को बदलने की आवश्यकता होती है।
C # में एक स्केच:
public class DiscardDraftDocumentCommandHandler : CommandHandler<DiscardDraftDocument>
{
IDraftDocumentRepository _repo;
IEventPublisher _publisher;
public DiscardDraftCommandHandler(IDraftDocumentRepository repo, IEventPublisher publisher)
{
_repo = repo;
_publisher = publisher;
}
public override void Handle(DiscardDraftDocument command)
{
var document = _repo.Get(command.DocumentId);
document.Discard(command.UserId);
_publisher.Publish(document.NewEvents);
}
}
document
डोमेन वस्तु व्यापार के नियम (जैसे या "आप एक दस्तावेज है कि पहले से ही खारिज कर दिया गया है त्यागने नहीं कर सकते" "उपयोगकर्ता दस्तावेज़ त्यागने के लिए अनुमति होनी चाहिए") लागू करने के लिए और डोमेन घटनाओं हम प्रकाशित करने की जरूरत पैदा करने के लिए जिम्मेदार है ( document.NewEvents
होगा हो सकता है ( IEnumerable<Event>
और शायद एक DocumentDiscarded
घटना होगी)।
यह एक अच्छा डिज़ाइन है - इसका विस्तार करना आसान है (आप अपने डोमेन मॉडल को बदले बिना नए उपयोग के मामलों को जोड़ सकते हैं, नए कमांड हैंडलर्स को जोड़कर) और अज्ञेय हैं कि कैसे वस्तुओं को बरकरार रखा जाता है (आप आसानी से एक एनजीबेरनेट रिपॉजिटरी को मानगो के लिए स्वैप कर सकते हैं रिपॉजिटरी, या एक EventStore प्रकाशक के लिए एक RabbitMQ प्रकाशक को स्वैप करें) जो नकली और नकली का उपयोग करके परीक्षण करना आसान बनाता है। यह मॉडल / दृश्य पृथक्करण का भी पालन करता है - कमांड हैंडलर को यह पता नहीं है कि यह बैच नौकरी, जीयूआई या रीस्ट एपीआई द्वारा उपयोग किया जा रहा है या नहीं।
हास्केल जैसी विशुद्ध रूप से कार्यात्मक भाषा में, आप कमांड हैंडलर को मोटे तौर पर इस तरह से मॉडल कर सकते हैं:
newtype CommandHandler = CommandHandler {handleCommand :: Command -> IO Result)
data Result a = Success a | Failure Reason
type Reason = String
discardDraftDocumentCommandHandler = CommandHandler handle
where handle (DiscardDraftDocument documentID userID) = do
document <- loadDocument documentID
let result = discard document userID :: Result [Event]
case result of
Success events -> publishEvents events >> return result
-- in an event-sourced model, there's no extra step to save the document
Failure _ -> return result
handle _ = return $ Failure "I expected a DiscardDraftDocument command"
यहाँ मैं समझने के लिए संघर्ष कर रहा हूँ। आमतौर पर, कुछ प्रकार के 'प्रेजेंटेशन' कोड होंगे जो कमांड हैंडलर में कॉल करते हैं, जैसे कि GUI या REST API। तो अब हमारे कार्यक्रम में दो परतें हैं जिन्हें IO - कमांड हैंडलर और व्यू - जो कि हास्केल में एक बड़ी संख्या में नहीं है - करने की आवश्यकता है।
जहां तक मैं बाहर कर सकता हूं, यहां दो विरोधी ताकतें हैं: एक मॉडल / दृश्य अलगाव है और दूसरा मॉडल को बनाए रखने की आवश्यकता है। कहीं न कहीं मॉडल को बनाए रखने के लिए IO कोड की आवश्यकता होती है , लेकिन मॉडल / दृश्य पृथक्करण कहता है कि हम इसे अन्य सभी IO कोड के साथ प्रस्तुति परत में नहीं डाल सकते।
बेशक, एक "सामान्य" भाषा में, आईओ कहीं भी हो सकता है (और करता है)। अच्छा डिज़ाइन तय करता है कि विभिन्न प्रकार के IO को अलग रखा जाए, लेकिन संकलक इसे लागू नहीं करता है।
तो: हम कार्यक्रम के बहुत किनारे तक IO कोड को पुश करने की इच्छा के साथ मॉडल / दृश्य पृथक्करण कैसे करते हैं, जब मॉडल को बनाए रखने की आवश्यकता होती है? हम दो अलग-अलग प्रकार के IO को कैसे अलग रखते हैं , लेकिन फिर भी सभी शुद्ध कोड से दूर रहते हैं?
अद्यतन : इनाम 24 घंटे से कम समय में समाप्त हो रहा है। मुझे नहीं लगता है कि वर्तमान उत्तरों में से किसी ने भी मेरे प्रश्न को संबोधित किया है। @ Ptharien की लौ के बारे में टिप्पणी acid-state
आशाजनक लगती है, लेकिन यह एक जवाब नहीं है और इसकी विस्तार से कमी है। मुझे बर्बाद करने के लिए इन बिंदुओं से नफरत होगी!
acid-state
बहुत अच्छा लग रहा है, उस लिंक के लिए धन्यवाद। एपीआई डिजाइन के संदर्भ में यह अभी भी बाध्य है IO
; मेरा प्रश्न यह है कि कैसे एक दृढ़ता का ढांचा एक बड़ी वास्तुकला में फिट बैठता है। क्या आप किसी भी ओपन-सोर्स एप्लिकेशन के बारे में जानते हैं acid-state
जो एक प्रेजेंटेशन लेयर के साथ उपयोग करते हैं, और दो को अलग रखने में सफल होते हैं?
Query
और Update
monads बहुत दूर से निकाल दिए जातेIO
हैं, वास्तव में। मैं एक उत्तर में एक सरल उदाहरण देने की कोशिश करूंगा।
acid-state
आप जो वर्णन कर रहे हैं, उसके करीब लगता है ।