Solana programs किसी विशेष कोडबेस संरचना (codebase structure) को लागू नहीं करते हैं, इसलिए कोड का संगठन अक्सर डेवलपर की पसंद और प्रोग्राम की जटिलता पर निर्भर करता है। वास्तव में, एक Solana program केवल एक lib.rs फ़ाइल के रूप में मौजूद हो सकता है, जैसा कि हमने अब तक इस सीरीज़ में देखा है।
लेकिन जैसे-जैसे आपका प्रोग्राम अधिक जटिल होता जाता है, आप लॉजिक और डेटा को प्रासंगिक फ़ाइलों और एक स्पष्ट फ़ोल्डर संरचना (folder structure) में अलग करना चाहेंगे ताकि कोड को आसानी से खोजा, मेंटेन और आगे बढ़ाया जा सके।
Solana डेवलपमेंट इकोसिस्टम प्रोग्राम के विभिन्न हिस्सों को व्यवस्थित करने के लिए एक सामान्य पैटर्न का पालन करता है। यह लेख सिखाता है कि Anchor और raw Solana programs दोनों के लिए इस पैटर्न का पालन करते हुए Solana program को कैसे व्यवस्थित किया जाए।
Solana program संरचना के घटक
Solana program की बुनियादी यूनिट संरचना
प्रत्येक Solana program एक Rust Cargo library crate है। इसका मतलब है कि डिफ़ॉल्ट संरचना एक Cargo प्रोजेक्ट के रूप में शुरू होती है, जिसमें Cargo.toml होता है जो डिपेंडेंसीज़ (dependencies) और बिल्ड कॉन्फ़िगरेशन को परिभाषित करता है, और lib.rs होता है जिसमें प्रोग्राम का लॉजिक शामिल होता है। आप cargo init --lib my_program कमांड चलाकर यह संरचना उत्पन्न कर सकते हैं और जनरेट की गई प्रोग्राम संरचना नीचे दिखाए अनुसार होगी:
my_program/
├── Cargo.lock
├── Cargo.toml
└── src
└── lib.rs
इसे Solana program बनाने के लिए, हमें नीचे दिए गए कोड को Cargo.toml फ़ाइल में जोड़ना होगा।
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
solana-program = "2.0.0"
ऊपर दिए गए कॉन्फ़िगरेशन का अर्थ यहां दिया गया है:
crate-type = ["cdylib", "lib"]के साथ[lib]अनुभाग Cargo को प्रोग्राम को एक डायनेमिक लाइब्रेरी (cdylib) के रूप में संकलित (compile) करने के लिए कहता है, जिसकी आवश्यकता Solana को.soफ़ाइल के रूप में डिप्लॉयमेंट के लिए होती है, और साथ ही लोकल टेस्टिंग या विभिन्न प्रोग्राम्स में लॉजिक को फिर से उपयोग करने के लिए एक मानक Rust लाइब्रेरी (lib) के रूप में संकलित करने के लिए भी कहता है।solana-program = "2.0.0"डिपेंडेंसी Solana SDK क्रेट्स (crates) को शामिल करती है, जो ऑन-चेन प्रोग्राम लिखने के लिए Solana के रनटाइम प्रकारों, मैक्रोज़ और हेल्पर फ़ंक्शंस तक पहुंच प्रदान करते हैं।
Solana program के घटक
Solana program कोडबेस की संरचना कैसे करें, यह ठीक से समझने के लिए, आइए पहले उन लॉजिकल घटकों को समझें जो एक Solana program बनाते हैं। प्रत्येक प्रोग्राम में आमतौर पर निम्नलिखित भाग शामिल होते हैं:
- Entry point: उस पहले फ़ंक्शन को परिभाषित करता है जिसे Solana रनटाइम कॉल करेगा।
- Instructions: यह परिभाषित करते हैं कि प्रोग्राम कौन से कार्य कर सकता है और इनपुट डेटा को कैसे सीरियलाइज़ और डीसीरियलाइज़ किया जाता है, अर्थात् फ़ंक्शंस के आर्ग्यूमेंट्स को कैसे संरचित किया जाता है।
- Instruction processing: उस लॉजिक को लागू करता है जो प्रत्येक निर्देश को निष्पादित करता है — यहीं पर मुख्य कंप्यूटेशन होता है।
- Accounts: ऑन-चेन डेटा लेआउट का वर्णन करते हैं। प्रत्येक खाता प्रकार (account type) यह निर्दिष्ट करता है कि वह कौन सी स्थिति (state) रखता है और इसे कैसे सीरियलाइज़ किया जाता है (आमतौर पर सीधे
borshक्रेट याAnchorमैक्रोज़ के साथ)। हमें उन खातों को भी परिभाषित करने की आवश्यकता होती है जिनके साथ हम इंटरैक्ट करेंगे। - Error handling: डिबगिंग और क्लाइंट-साइड त्रुटि व्याख्या को सरल बनाने के लिए विवरणात्मक त्रुटि कोड (descriptive error codes) प्रदान करता है।
- Tests: यह सत्यापित करते हैं कि जब आपके प्रोग्राम को स्थानीय रूप से डिप्लॉय किया जाता है, तो यह अपेक्षित व्यवहार करता है।
हम ऊपर दी गई अवधारणाओं का प्रतिनिधित्व करने वाली फ़ाइलों के साथ एक साधारण Solana program की संरचना कर सकते हैं, जैसा कि नीचे दिखाया गया है। इस संरचना में, lib.rs प्रोग्राम के रूट के रूप में कार्य करता है; यह मॉड्यूल्स को एक्सपोज़ करता है और उन्हें एक साथ लिंक करता है। entrypoint.rs फ़ाइल उस फ़ंक्शन को परिभाषित करती है जिसे Solana का रनटाइम कॉल करता है जब प्रोग्राम को इन्वोक किया जाता है। यह एंट्रीपॉइंट फ़ंक्शन, जिसे आमतौर पर process_instruction नाम दिया जाता है, एक डिस्पैचर (dispatcher) लागू करता है जो आने वाले प्रत्येक निर्देश को उसके संबंधित हैंडलर तक रूट करता है (जैसा कि पिछले ट्यूटोरियल में चर्चा की गई थी)।
शेष प्रत्येक फ़ाइल ऊपर वर्णित लॉजिकल घटकों में से एक से मेल खाती है।
program/
├── src/
│ ├── entrypoint.rs // Program entry point (process_instruction)
│ ├── instruction.rs // Instruction enum and data structures
│ ├── processor.rs // Business logic for each instruction
│ ├── state.rs // Account data structures
│ ├── error.rs // Custom error types
│ └── lib.rs // Module declarations and re-exports
हालाँकि, ये फ़ाइल नाम मनमाने हो सकते हैं, लेकिन उन नामों का उपयोग करना एक सामान्य परंपरा है जो लागू की जा रही अवधारणा से संबंधित हों।
उपरोक्त संरचना Anchor और raw Solana programs दोनों में समान है। मुख्य अंतर यह है कि Anchor स्वचालित रूप से एंट्रीपॉइंट और प्रोसेसर्स उत्पन्न करने के लिए मैक्रोज़ का उपयोग करता है, जबकि raw Solana programs में, आप इसे मैन्युअल रूप से परिभाषित करते हैं। यह संरचना सरल प्रोग्राम्स के लिए काम करती है, जैसे-जैसे आपका प्रोग्राम बड़ा होता जाएगा, आपके पास कई निर्देश, प्रोसेसर्स या स्टेट्स होंगे, जिसका अर्थ है कि आपको उन्हें फ़ोल्डर्स में व्यवस्थित करने की आवश्यकता हो सकती है।
आइए गहराई से जानें कि एक Solana program को कैसे व्यवस्थित किया जाना चाहिए।
Anchor प्रोजेक्ट संरचना
Anchor, raw Solana programs में आवश्यक अधिकांश बॉयलरप्लेट को एब्स्ट्रैक्ट करके Solana program विकास को सरल बनाता है। यह एक सुसंगत प्रोजेक्ट टेम्प्लेट भी प्रदान करता है जो प्रोग्राम लिखने से लेकर उसे बिल्ड करने, टेस्ट करने और डिप्लॉय करने तक संपूर्ण वर्कफ़्लो का समर्थन करता है।
नीचे एक विशिष्ट Anchor प्रोजेक्ट की संरचना दी गई है, जिसे आप इस सीरीज़ में पहले ही कई बार देख चुके हैं।
├── Anchor.toml
├── app
├── Cargo.lock
├── Cargo.toml
├── migrations
│ └── deploy.ts
├── package.json
├── programs
│ ├── hello-program
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── Xargo.toml
│ ├── token_vault
│ ├── Cargo.toml
│ ├── src
│ │ └── lib.rs
│ └── Xargo.toml
├── tests
│ └──hello-program.ts
├── tsconfig.json
└── yarn.lock
इस संरचना का प्रत्येक भाग Anchor के वर्कफ़्लो में एक विशिष्ट भूमिका निभाता है:
Anchor.tomlफ़ाइल बिल्ड और डिप्लॉयमेंट सेटिंग्स को परिभाषित करती हैmigrationsफ़ोल्डर डिप्लॉयमेंट स्क्रिप्ट्स को संग्रहीत करता हैtestsनिर्देशिका में इंटीग्रेशन टेस्ट्स शामिल होते हैं जो लोकल वैलिडेटर पर चलते हैंappनिर्देशिका में क्लाइंट कोड हो सकता है जो डिप्लॉय किए गए प्रोग्राम के साथ इंटरैक्ट करता है
ध्यान दें कि Anchor में programs निर्देशिका में सब-डायरेक्टरीज़ होती हैं जो उसी बुनियादी Cargo बॉयलरप्लेट संरचना को दर्शाती हैं जिस पर हमने पहले चर्चा की थी, जो केवल एक प्रोग्राम के लिए थी।
एक Anchor प्रोजेक्ट इस तरह से संरचित होता है कि आप एक प्रोजेक्ट के भीतर कई ऑन-चेन Solana programs पर काम कर सकें। नीचे दिया गया चित्र एक विशिष्ट Rust Cargo प्रोजेक्ट की तुलना इस बात से करता है कि Anchor अपने प्रोजेक्ट्स को कैसे व्यवस्थित करता है।

Anchor और raw Solana संरचनाओं के बीच अंतर
- Anchor इन फ़ाइलों को तत्काल संकलन के लिए पूरी तरह से कॉन्फ़िगर करके जनरेट करता है। आप प्रोजेक्ट बनाने के तुरंत बाद
anchor buildचला सकते हैं और एक कार्यशील प्रोग्राम बाइनरी प्राप्त कर सकते हैं। - Cargo के साथ raw Solana program सेटअप के लिए, आप
Cargo.tomlमें डिपेंडेंसीज़ और क्रेट टाइप सेटिंग्स जोड़कर Cargo बॉयलरप्लेट को मैन्युअल रूप से Solana program के रूप में कॉन्फ़िगर करते हैं, जैसा कि हमने पहले चर्चा की थी। और यदि आप एक प्रोजेक्ट में कई प्रोग्राम चलाना चाहते हैं, तो आपको Anchor कीprogramsनिर्देशिका की तरह ही एकprogramsनिर्देशिका की आवश्यकता होगी। प्रोग्राम निर्देशिकाओं को रखने के लिएprogramsनाम का उपयोग करना एक परंपरा है, भले ही आप किसी भी नाम का उपयोग कर सकते हैं, और आप प्रत्येक प्रोग्राम को उसकी अपनीCargo.tomlफ़ाइल के माध्यम से कॉन्फ़िगर करते हैं।
ध्यान दें कि Xargo.toml फ़ाइलें Anchor की जनरेट की गई प्रोजेक्ट संरचना में हैं।

वे Anchor की डिफ़ॉल्ट कॉन्फ़िगरेशन फ़ाइल हैं जो इस बात को संभालती हैं कि Anchor प्रोग्राम्स को Extended Berkeley Packet Filter (eBPF) bytecode में कैसे संकलित किया जाता है। हम अगले अनुभाग में इसके बारे में अधिक जानेंगे।
Anchor eBPF के लिए क्रॉस-कंपाइलेशन (cross-compilation) को कैसे संभालता है
प्रत्येक प्रोग्राम के अंदर Xargo.toml फ़ाइल Rust को बताती है कि Solana के ब्लॉकचेन वातावरण के लिए आपके कोड को कैसे संकलित किया जाए। आपका Solana program वैलिडेटर्स पर Solana Virtual Machine (SVM) में चलता है, जो Extended Berkeley Packet Filter (eBPF) bytecode को निष्पादित करता है।
जब आप अपनी मशीन पर Rust कोड लिखते हैं और उसे संकलित करते हैं, तो Rust कंपाइलर सामान्यतः आपके कंप्यूटर के प्रोसेसर (जैसे x86 या ARM) के लिए निर्देश उत्पन्न करता है। लेकिन Solana वैलिडेटर्स उन निर्देशों को निष्पादित नहीं कर सकते हैं। वे केवल eBPF बाइटकोड समझते हैं।
एक अलग आर्किटेक्चर के लिए इस संकलन प्रक्रिया को क्रॉस-कंपाइलेशन कहा जाता है। Xargo.toml फ़ाइल यह निर्दिष्ट करती है कि Rust कंपाइलर को आपकी स्थानीय मशीन के बजाय eBPF के लिए आपका कोड कैसे बनाना चाहिए। यह फ़ाइल नियंत्रित करती है कि अंतिम संकलित प्रोग्राम में Rust की मानक लाइब्रेरी के कौन से भाग शामिल किए जाएंगे।
Solana प्रोग्राम्स की सख्त आकार सीमा (10 MB) होती है, इसलिए Xargo.toml कॉन्फ़िगरेशन यह सुनिश्चित करता है कि संकलित प्रोग्राम में केवल वही शामिल हो जिसकी आपके प्रोग्राम को आवश्यकता है। जब आप anchor init {project-name} या anchor new {program-name} चलाते हैं तो Anchor स्वचालित रूप से Xargo.toml फ़ाइल उत्पन्न करता है। आपको इसे संशोधित करने की आवश्यकता नहीं होगी। Xargo.toml फ़ाइल की सामग्री कुछ इस तरह दिखती है:
[target.bpfel-unknown-unknown.dependencies.std]
features = []
लक्ष्य नाम bpfel-unknown-unknown एक Rust compilation target triple है। यह Rust कंपाइलर को बताता है कि यह किस प्रकार की मशीन और वातावरण के लिए निर्माण कर रहा है। इसके 3 भाग हैं, जो हाइफ़न द्वारा अलग किए गए हैं। यहाँ प्रत्येक भाग का अर्थ दिया गया है:
bpfel- लिटिल-एंडियन बाइट ऑर्डरिंग के साथ BPF आर्किटेक्चर के लिए संकलित करेंunknown- पहले unknown का अर्थ है, किसी भी विशिष्ट OS के लिए संकलित न करेंunknown- अंतिम unknown का अर्थ है, किसी भी विशिष्ट ABI (Application Binary Interface) के लिए संकलित न करें
खाली features ( features = []) ऐरे का मतलब है कि आप ब्लॉकचेन डिप्लॉयमेंट के लिए अनुकूलित मानक लाइब्रेरी के न्यूनतम संस्करण का उपयोग कर रहे हैं।
Raw Solana programs क्रॉस-कंपाइलेशन को अलग तरीके से संभालते हैं। आप अपने प्रोग्राम को cargo-build-sbf के साथ बिल्ड करते हैं, जो अलग Xargo.toml फ़ाइल की आवश्यकता के बिना आपके Rust कोड को eBPF बाइटकोड में संकलित करता है।
Anchor एकाधिक प्रोग्राम्स (multiple programs) की संरचना कैसे करता है
एक प्रोजेक्ट में कई प्रोग्राम्स को प्रबंधित करने के लिए Anchor Cargo वर्कस्पेस का उपयोग करता है। एक वर्कस्पेस आपको कई संबंधित प्रोग्राम्स पर काम करने की अनुमति देता है जो डिपेंडेंसीज़ और बिल्ड कॉन्फ़िगरेशन साझा करते हैं।
Anchor विभिन्न उद्देश्यों के साथ दो प्रकार की Cargo.toml फ़ाइलों का उपयोग करता है। रूट Cargo.toml वर्कस्पेस संरचना और साझा बिल्ड कॉन्फ़िगरेशन को परिभाषित करता है। प्रत्येक प्रोग्राम निर्देशिका में अपना स्वयं का Cargo.toml होता है जो उस विशिष्ट प्रोग्राम की डिपेंडेंसीज़ की घोषणा करता है।
भले ही दो Solana programs एक ही Rust क्रेट का उपयोग करते हों, प्रत्येक को इसे प्रोग्राम की निर्देशिका के अंदर Cargo.toml फ़ाइल में अलग से घोषित करना होगा। आप वर्कस्पेस स्तर पर एक बार डिपेंडेंसी घोषित करके सभी प्रोग्राम्स में उसे इनहेरिट नहीं करा सकते।
यदि एक ही Anchor वर्कस्पेस में दो प्रोग्राम्स, मान लीजिए A और B, एक ही क्रेट पर निर्भर करते हैं, तो वे इसका अलग-अलग तरीके से उपयोग कर सकते हैं। प्रोग्राम A एक लाइब्रेरी फ़ंक्शन का उपयोग कर सकता है जबकि प्रोग्राम B पांच का उपयोग करता है। यह प्रभावित करता है कि संकलन के दौरान लाइब्रेरी के कितने हिस्से को प्रून (हटाया) किया जा सकता है।
लेकिन प्रोग्राम्स बिल्ड कॉन्फ़िगरेशन साझा करते हैं। वर्कस्पेस डिपेंडेंसीज़ और बिल्ड कॉन्फ़िगरेशन आपके Anchor प्रोजेक्ट रूट में स्थित Cargo.toml फ़ाइल में परिभाषित किए जाते हैं, जिसमें कई डिफ़ॉल्ट सेटिंग्स भी होती हैं जैसा कि नीचे दिए गए स्क्रीनशॉट में दिखाया गया है।
रूट निर्देशिका में Cargo.toml फ़ाइल का [workspace] अनुभाग एक members ऐरे को परिभाषित करता है जो उन विशिष्ट निर्देशिकाओं को परिभाषित करता है जो वर्कस्पेस का हिस्सा हैं। ध्यान दें कि इसमें [”programs/”] शामिल है जो आपकी डिफ़ॉल्ट Solana programs निर्देशिका का नाम है। नीचे दी गई Cargo फ़ाइल एक रूट Cargo.toml फ़ाइल है:

रूट Cargo.toml फ़ाइल में प्रत्येक अनुभाग का अर्थ यहाँ दिया गया है:
[workspace] अनुभाग यह परिभाषित करता है कि कौन सा प्रोग्राम वर्कस्पेस से संबंधित है और यह नियंत्रित करता है कि Cargo कई संबंधित पैकेजों में डिपेंडेंसीज़ को कैसे संभालता है।
members = ["programs/*"]-programsके अंदर की प्रत्येक सब-डायरेक्टरी इस वर्कस्पेस से संबंधित हैresolver = "2"- यह सेटिंग Cargo को अपने नए डिपेंडेंसी रिज़ॉल्यूशन एल्गोरिथम (version 2) का उपयोग करने के लिए कहती है
[profile.release] अनुभाग रिलीज़ मोड में बिल्ड करते समय संकलन सेटिंग्स को नियंत्रित करता है। यह आपको ऑप्टिमाइज़ेशन स्तर, डिबग जानकारी और कोड जनरेशन व्यवहार को कॉन्फ़िगर करने की अनुमति देता है
overflow-checks = true- रिलीज़ बिल्ड्स में अंकगणितीय ओवरफ़्लो चेक्स को सक्षम रखता है, जिससे इंटीजर ओवरफ़्लो बग्स को रोका जा सकता है जो आपके प्रोग्राम की स्थिति को दूषित कर सकते हैंlto = "fat"- यह सेटिंग लिंक-टाइम ऑप्टिमाइज़ेशन को सक्षम करती है, जो कंपाइलर लिंकिंग (एक प्रक्रिया जहां कंपाइलर सभी संकलित कोड को अंतिम निष्पादन योग्य में मिलाता है) के दौरान पूरे प्रोग्राम का विश्लेषण करती है ताकि अप्रयुक्त कोड और इनलाइन फ़ंक्शंस को हटाया जा सके, जिससे अंतिम बाइनरी का आकार कम हो जाता है। यदि हम तेज़ LTO चाहते हैं लेकिन कम ऑप्टिमाइज़ेशन के साथ, तो हमltoपैरामीटर कोthinपर सेट कर सकते हैंcodegen-units = 1- आपके पूरे प्रोग्राम को संकलन कार्य को कई समानांतर चंक्स में विभाजित करने के बजाय एक पास में संकलित करता है, मान जितना अधिक होगा, संकलन उतने ही अधिक चंक्स में विभाजित होगा। इसे1पर सेट करने से कंपाइलर एक ही बार में पूरे कोडबेस में ऑप्टिमाइज़ेशन कर पाता है, जिससे छोटी और अधिक अनुकूलित बाइनरीज़ बनती हैं, लेकिन इसकी कीमत यह है कि संकलन में अधिक समय लगता है। डिफ़ॉल्ट Rust सेटिंगcodegen-units = 16है, जो बिल्ड को तेज़ करती है लेकिन इससे थोड़ी बड़ी बाइनरीज़ बन सकती हैं।
[profile.release.build-override] अनुभाग विशेष रूप से बिल्ड स्क्रिप्ट्स के लिए संकलन सेटिंग्स निर्दिष्ट करता है, जो वे प्रोग्राम हैं जो कोड उत्पन्न करने या बिल्ड को कॉन्फ़िगर करने के लिए आपके मुख्य कोड के संकलित होने से पहले चलते हैं।
opt-level = 3- बिल्ड स्क्रिप्ट्स पर अधिकतम ऑप्टिमाइज़ेशन लागू करता हैincremental = false- इंक्रीमेंटल संकलन को अक्षम करता है। प्रत्येक संकलन शुरू से अंत तक किया जाता है। यह संकलन के समय को धीमा कर देता है लेकिन बचे हुए आर्टिफैक्ट्स द्वारा संकलन प्रक्रिया को खराब करने के जोखिम को कम करता हैcodegen-units = 1- बिल्ड स्क्रिप्ट्स पर समान सिंगल-यूनिट ऑप्टिमाइज़ेशन लागू करता है
Anchor स्वतः-उत्पन्न (Auto-generated) कोड
संपूर्ण Anchor प्रोजेक्ट और इसके भीतर के व्यक्तिगत प्रोग्राम्स में कोई स्पष्ट entrypoint.rs और processor.rs फ़ाइलें शामिल नहीं होती हैं जो Solana प्रोजेक्ट्स में प्रमुख आवश्यकताएं हैं। जैसा कि हमने पहले चर्चा की है, lib.rs फ़ाइल के अंदर #[program] एट्रिब्यूट स्वचालित रूप से एंट्रीपॉइंट स्रोत कोड के साथ-साथ इंस्ट्रक्शन डिकोडिंग, डिस्पैचिंग और अकाउंट डीसीरियलाइज़ेशन (जो आम तौर पर raw Solana programs में processor.rs फ़ाइल में होगा) को संभालने के लिए लॉजिक उत्पन्न करता है।
नीचे दिया गया कोड दिखाता है कि Anchor प्रोग्राम्स में #[program] एट्रिब्यूट का उपयोग कैसे किया जाता है:
#[program]
pub mod hello_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
msg!("Program initialized!");
Ok(())
}
}
Anchor 1.0 में बेहतर प्रोजेक्ट संगठन (organization)
Anchor 1.0 एक प्रोजेक्ट लेआउट पेश करता है जो प्रत्येक प्रोग्राम के अंदर बेहतर फ़ाइल संगठन को प्रोत्साहित करता है।
Anchor 1.0 में anchor init चलाने से सभी मानक प्रोजेक्ट घटक जैसे निर्देश, स्थिति, त्रुटियां और स्थिरांक अलग-अलग मॉड्यूल में उत्पन्न होंगे।
यह परिवर्तन नए डेवलपर्स को बड़े Solana programs को व्यवस्थित करने के लिए अधिक स्वच्छ पैटर्न सीखने में मदद करता है। आप अभी भी --template single फ़्लैग (anchor init --template single ) का उपयोग करके पुराना सिंगल-फ़ाइल लेआउट उत्पन्न कर सकते हैं।
यहाँ एक उदाहरण दिया गया है कि नई डिफ़ॉल्ट संरचना कैसी दिखेगी:
programs
└── vote
├── Cargo.toml
└── src
├── instructions
│ ├── initialize.rs
│ └── mod.rs
├── state
│ └── mod.rs
├── constants.rs
├── error.rs
└── lib.rs
ध्यान देने योग्य दो नई चीज़ें हैं: प्रत्येक निर्देशिका के अंदर mod.rs फ़ाइलें और initialize.rs इंस्ट्रक्शन मॉड्यूल। हम बाद के अनुभाग में उन पर चर्चा करेंगे।
इस नई संरचना के साथ, अब आप एक ही lib.rs फ़ाइल के साथ एक प्रोग्राम शुरू नहीं करते हैं जो खातों और निर्देशों को मिलाता है। इसके बजाय, आपका स्टेट अपनी स्वयं की निर्देशिका में रहता है, और प्रत्येक निर्देश अपने स्वयं के मॉड्यूल में स्थित होता है।
नीचे दिया गया उदाहरण पुराने सिंगल-फ़ाइल पैटर्न को दिखाता है जिसका उपयोग Anchor डिफ़ॉल्ट रूप से करता था, जहाँ प्रोग्राम लॉजिक और खाता प्रकार lib.rs के अंदर एक साथ बैठते हैं।
use anchor_lang::prelude::*;
declare_id!("6uAEFiYjmgJhCCqw8JPH8chZRWJPzHFBJYuZFMWaML3w");
#[program]
pub mod program_structure {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
msg!("Greetings from: {:?}", ctx.program_id);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
मानक (Standard) Solana program संरचना
इस बिंदु पर, आपने देखा है कि Solana program का प्रत्येक भाग क्या करता है और Anchor उन्हें स्वचालित रूप से कैसे व्यवस्थित करता है। लेकिन Anchor के बिना भी, आप एक सुसंगत, मॉड्यूलर लेआउट का पालन कर सकते हैं जो यह सुनिश्चित करता है कि आपका प्रोग्राम मेंटेन करने योग्य है।
हमने अध्ययन किया है कि कुछ शीर्ष Solana प्रोजेक्ट्स अपने प्रोग्राम्स की संरचना कैसे करते हैं और एक सुसंगत पैटर्न देखा है जो सहयोग, रखरखाव और स्केलेबिलिटी को बढ़ावा देता है। एक विशिष्ट Solana program नीचे दी गई संरचना का पालन करता है, जिसमें एंट्रीपॉइंट और प्रोसेसर निर्देशिकाएं स्पष्ट रूप से केवल raw Solana programs में परिभाषित की जाती हैं।
program/
├── Cargo.toml
└── src/
├── entrypoint.rs
├── instructions/
│ ├── mod.rs
│ ├── initialize.rs
│ └── transfer.rs
├── processor/
│ ├── mod.rs
│ ├── initialize.rs
│ └── transfer.rs
├── state/
│ ├── mod.rs
│ ├── account.rs
│ └── config.rs
├── error.rs
├── utils/
│ ├── mod.rs
│ ├── pda.rs
│ ├── math.rs
│ └── validation.rs
└── lib.rs
हमने नई फ़ाइलें पेश कीं: प्रत्येक निर्देशिका के लिए mod.rs और पिछले अनुभाग तथा ऊपर दी गई संरचना में initialize.rs। आइए उन्हें समझाते हैं:
mod.rs फ़ाइल
mod.rs फ़ाइल Rust में एक निर्देशिका के लिए मॉड्यूल घोषणा बिंदु के रूप में कार्य करती है। जब आप संबंधित कोड को instructions/ जैसे फ़ोल्डर में व्यवस्थित करते हैं, तो Rust स्वचालित रूप से इसके अंदर की फ़ाइलों को आपके प्रोग्राम के हिस्से के रूप में नहीं पहचानता है। आपको स्पष्ट रूप से Rust को यह बताना होगा कि किन फ़ाइलों को संकलित किया जाना चाहिए और आपके कोडबेस के अन्य हिस्सों तक पहुंच योग्य बनाया जाना चाहिए, यही mod.rs का काम है (और यह मनमाना नहीं है, एक निर्देशिका के माध्यम से एक मॉड्यूल को परिभाषित करते समय Rust इस सटीक फ़ाइल नाम की अपेक्षा करता है)।
यहाँ बताया गया है कि instructions/mod.rs कैसा दिखता है:
pub mod initialize;
pub mod transfer;
प्रत्येक पंक्ति निर्देशिका में एक फ़ाइल को एक मॉड्यूल के रूप में घोषित करती है। pub कीवर्ड इन मॉड्यूल्स को instructions निर्देशिका के बाहर सार्वजनिक रूप से सुलभ बनाता है। इन घोषणाओं के बिना, Rust initialize.rs या transfer.rs को संकलित नहीं करेगा, और आपके प्रोग्राम के अन्य भाग उनकी सामग्री को आयात नहीं कर सकते हैं।
अपनी lib.rs फ़ाइल में, आप फिर instructions मॉड्यूल को एक्सपोज़ करते हैं ताकि यह आपके पूरे प्रोग्राम में उपलब्ध हो सके:
pub mod instructions;
pub use instructions::*;
यह पैटर्न आपके प्रोग्राम के प्रत्येक डायरेक्टरी के लिए दोहराया जाता है।
processor/mod.rsफ़ाइल प्रोसेसर मॉड्यूल्स की घोषणा करती हैstate/mod.rsस्टेट मॉड्यूल्स की घोषणा करती है- और
utils/mod.rsयूटिलिटी मॉड्यूल्स की घोषणा करती है।
instructions/initialize.rs फ़ाइल
Anchor की lib.rs फ़ाइल में initialize फ़ंक्शन आपके निर्देशों के लिए आपके प्रोग्राम की प्रारंभिक स्थिति स्थापित करने की एक Anchor परंपरा है। हम अपनी lib.rs फ़ाइल को स्वच्छ रखने के लिए उस स्टेट इनिशियलाइज़ेशन को किसी भिन्न फ़ाइल में स्थानांतरित कर सकते हैं।
डिफ़ॉल्ट जनरेट किया गया initialize फ़ंक्शन lib.rs फ़ाइल में इस तरह दिखता है:

यहाँ बताया गया है कि आप Anchor प्रोजेक्ट्स के लिए अपनी lib.rs फ़ाइल में initialize फ़ंक्शन का उपयोग कैसे करेंगे जब आपने इसे एक समर्पित initialize.rs फ़ाइल में ले जाया है और इसे instructions निर्देशिका में एक मॉड्यूल बना दिया है:

processors निर्देशिका
raw Solana में, processor/ निर्देश हैंडलर्स को लागू करता है। प्रत्येक प्रोसेसर मॉड्यूल instructions/ में एक इंस्ट्रक्शन मॉड्यूल से मेल खाता है।
मॉड्यूलर की गई (Modularized) Anchor संरचना
व्यक्तिगत प्रोग्राम्स के लिए एक मानक Anchor शैली प्रोग्राम संरचना बिना किसी एंट्रीपॉइंट या प्रोसेसर फ़ाइल के नीचे दी गई संरचना की तरह दिखेगी। यह संरचना Anchor संस्करण 1.0 प्रोजेक्ट संरचना को दर्शाती है:
programs/
└── my_program/
├── Cargo.toml
└── src/
├── lib.rs
├── instructions/
│ ├── transfer.rs
│ └── mod.rs
├── state/
│ ├── config.rs
│ ├── account.rs
│ └── mod.rs
├── error.rs
├── utils/
│ ├── mod.rs
│ ├── pda.rs
│ ├── math.rs
│ └── validation.rs
इस संरचना के साथ, डेवलपर्स के लिए संबंधित लॉजिक का पता लगाना, प्रोग्राम फ़्लो को समझना और मौजूदा व्यवहार को तोड़े बिना कार्यक्षमता का विस्तार करना आसान हो जाता है, चाहे वह raw Solana program हो या Anchor।
यह लेख Solana development पर एक ट्यूटोरियल सीरीज़ का हिस्सा है।