يتصل:
cargo run
من جذر المستودع وأنت مستعد للذهاب.
حاول أيضًا استكشاف مستندات API للحصول على فكرة عن المكان الذي يعيش فيه كل شيء:
cargo doc --open
يحتوي هذا المستودع على تطبيق صدأ عينة لمتجر عبر الإنترنت. الهدف من ذلك هو استكشاف بعض أنماط التصميم التي تستفيد من لغة الصدأ لإنشاء تطبيقات قابلة للتطوير يمكن الحفاظ عليها.
إنه ملعب لأفكار مختلفة ، بعضها قد لا يخرج في الممارسة العملية. إذا كان لديك أي تعليقات على أي شيء هنا ، فلا تتردد في فتح مشكلة!
من الصعب تصميم البرامج في فراغ. عندما لا يكون لديك مجال حقيقي لدفع ما هو مهم ، يمكن أن تشعر قرارات التصميم التعسفية. لقد بذلت جهدًا لتوثيق القرارات والأسباب الكامنة وراءها ، ولكن هل يجب أن نقسم عناصر الطلب من الطلبات؟ أو هل يجب أن تكون الاستفسارات في الطلبات قادرة على الوصول إلى جداول قاعدة البيانات للمنتجات؟ لا يمكن الرد عليها حقًا من وجهة نظر تقنية بحتة. أنها تتطلب منظور حول أهداف المشروع أيضا. بالنسبة لأي شخص يقرأ هذا الرمز ، أشجعك على التدقيق فيه بناءً على قرارات التصميم التعسفي هذه ، والتفكير في القيود التي تواجهها في بيئتك الخاصة وكيف يمكن أن تُعلم هذه القرارات الخاصة بك عند بناء الطلبات في الصدأ.
لا يتعلق الأمر بأطر عمل أو مكتبات محددة ، أو حول حل المشكلات المتأصلة في تطبيق التسوق عبر الإنترنت.
تصف الأقسام التالية أجزاء من التطبيق وشرح سبب تجميعها بالطريقة التي هي عليها.
يركز تخطيط المشروع على الخصوصية. عن طريق الحد من نطاق بعض العناصر ، فإنك تحد أيضًا من نطاق الكسر المحتمل. من خلال الحد من نطاق بعض العناصر ، يمكنك أيضًا الحد من نطاق عبء الحفاظ على حالة التطبيق. في الصدأ ، تكون العناصر الخاصة في وحدة نمطية مرئية لجميع أطفال تلك الوحدة . قد يبدو هذا شيئًا سيئًا ، لكننا نستفيد منه لمنع واجهات برمجة التطبيقات من المجال من تسرب تفاصيل التنفيذ من أجل المخاوف الخارجية ، مثل التسلسل والتخزين.
يتم تقسيم كل مفهوم أعمال أساسي في التطبيق إلى مجلد خاص به (في الغالب) ، مثل products أو customers . كل وحدة تغلف كل شيء يمكن معرفته عن مجموعة معينة من الكيانات:
/store )/queries )/commands ) يمكن أن تعتمد الكيانات على الكيانات من وحدة أخرى ، مثل Order اعتمادًا على Product عند إضافته. هناك تسلسل هرمي خصوصية في كل وحدة مجال:
from_datafrom_data إلى كيانات ترطيبهذه الوحدات هي وزن ثقيل بعض الشيء ، ولكن في تطبيق مناسب يمكن إضافة وحدات مجال جديدة يمكن تبسيطها باستخدام وحدات الماكرو. لم أستخدم وحدات الماكرو في هذا التطبيق ، لذا يظل الرمز سهل المتابعة.
تتمثل إحدى المشكلات في التسلسل الهرمي للوحدة النمطية المصممة تمامًا إلى أنه يمكن أن ينهار جميعًا عندما ينتهي بك الأمر بمفهوم لا يتناسب ببساطة في التصميم الحالي. كلما حدث ذلك بشكل متكرر ، كلما أصبح الأمر أكثر صعوبة في التوافق مع التصميم الموجود من قبل لأنه من المستحيل معرفة ما يجب أن يكون.
نريد أن تدير هذه الوحدات مصيرها ، لكننا لا نريد أن يكونوا مكتفين بذاتها لدرجة أنه يمكن تقسيمها إلى خدمات منفصلة. هذا هو الحفاظ على الأمور بسيطة. إذا كنت ترغب في القيام بذلك ، فسأقترح استخدام صناديق منفصلة بدلاً من وحدات منفصلة فقط.
يتبع التطبيق تصميم فصل مسؤولية الاستعلام البسيط. هذا هو النهج الذي يعمل بشكل جيد للتطبيق القائم على البيانات دون الكثير من المنطق المعقد. تأخذ الأوامر بعض تفاعل المجال والعمل مباشرة على الكيانات في حين أن الاستعلامات تعسفية تمامًا. لا يستخدم هذا التطبيق أي بنية تحتية خاصة لتحقيق CQRs ، فهي مجرد سمات بسيطة يتم تنفيذها باستخدام نمط حقن التبعية. بشكل أساسي:
Result<()>Result<T>&mut self&selfتعني الفرق في قابلية التكيف أن الأوامر يمكنها استدعاء الاستعلامات ولكن لا يمكن للاستعلامات استدعاء الأوامر.
الكيانات هي قلب التطبيق. على الرغم من عدم وجود عمل حقيقي ، بذلت جهدًا للحفاظ على نموذج النطاق غنيًا. الكيانات ليست مجرد أكياس من حالة كرودي. هم:
.to_data() . أثناء عرض كيان ، لا يمكنك استدعاء السلوك المعدل عليه. هذا مضمون من قبل نظام الاقتراض الصدأ. يمكن للكيان نقل الملكية إلى بياناته للقراءة فقط مع .into_data() . هذه عملية في اتجاه واحد ، لذلك لا يمكن استمرار أي تغييرات تم إجراؤها على الدولة إلى المتجر.الهدف من الكيان هو تغليف ثبات بعض مفهوم المجال الرئيسي. الكيانات هنا سهلة الاستخدام إما مع متجر وهمية في الذاكرة أو قاعدة بيانات خارجية. يجب أن نكون حريصين على عدم الاعتماد على تغييرات الحالة مع انعكاس كيان واحد في آخر لأنها تشير إلى نفس المصدر.
تحتاج الكيانات أيضًا إلى توخي الحذر من عدم الاعتماد على أنواع بيانات كيان آخر لأنه لا يوجد ضمان بأن البيانات صالحة بالفعل. بدلاً من ذلك ، يعتمدون على كيان ويحولونه إلى بيانات حسب الحاجة ، لذلك يعرفون دائمًا أن الحالة صالحة.
نستخدم ميزات الصدأ التالية لحماية حالة الكيان لدينا:
Serialize أو Deserialize . قد يتم تغيير هذا إلى أسفل المسار ، لكنني أجد أنه من الأسهل الحفاظ على قابلة للتسلسل سريعًا وأصبحًا سريعًا للتوافق مع الوراء.تقوم الكيانات بتغليف بعض الحالة أو البيانات وضمان أي تغييرات تم إجراؤها على تلك البيانات لا تكسر أي ثبات تتوقع البيانات الاحتفاظ بها. بدلاً من تنفيذ getters ، نقوم بفضح عرض للقراءة فقط للبيانات كهيكل. الفائدة هي أنه ليس عليك التخلي عن ميزات Rust الرائعة للعمل مع بنية البيانات ، كما تفعل مع أساليب Getter. هذا العرض هو للقراءة فقط ، لذلك لا يمكن كتابة التغييرات مباشرة إلى الهيكل. لا يزال الكيان يوفر طرقًا لذلك.
يمكنك القول أن تعريض الحالة بهذه الطريقة يتسرب تفاصيل التنفيذ ، مثل version الذي ليس له قيمة عامة. ربما هذا صحيح. للعمل من حوله ، يمكنك تحريك عمر الرأي القراءة فقط على الحقول ، وتكوين وجهة نظر مستعارة مختلفة محتملة للدولة ، والحفاظ على بنية البيانات التي يديرها الكيان الخاص.
يمكنك أيضًا القول بأن عقد الثوابات على هيكل لا يخزنهم أمر هش. هذا أمر منطقي عندما تكون حدود الخصوصية لبعض الحقل على مستوى الكائن ، كما هو الحال في C#. الصدأ مختلف بعض الشيء. حدود الخصوصية الأكثر ضيقًا في الوحدة وأطفالها . لذا فإن عبء الحفاظ على ثبات حقل معين يقع على جميع العناصر الموجودة في الوحدة التي يتم تعريفها فيها ، بالإضافة إلى كل أطفال تلك الوحدة.
قد يبدو هذا بمثابة تسرب فظيع ، لكن هذا التطبيق يستغل ذلك لبناء سعة تخزين مجردة بشكل جيد. بدلاً من الاضطرار إلى فضح الثقوب في واجهة برمجة التطبيقات الخاصة بنا لدعم ORM ، يمتد الحفاظ على حالة الثوابات ببساطة إلى متجر النماذج ، دون التسرب إلى الجمهور.
أنواع Id Version على حد سواء لها معلمة عامة phantom. توجد هذه المعلمة بحتة للسماح لك بالتعبير عن معرفات مع أنواع غير متوافقة ، مثل Id<ProductData> و Id<OrderData> ، ولكن لا تزال تشترك في تفاصيل التنفيذ الأخرى.
إنه نمط يسهل متابعته من استخدام الماكرو لتقليل لوحة الغلاية لأن هناك دائمًا صرف في المصدر الذي يمكنك العودة إليه.
كل كيان مستمر لديه حقل version . هذا الحقل هو معرف غير متسلسل يتوافق مع حالة الكيان في وقت معين. عندما يتم جلب كيان من المتجر ، فإننا نرطب نسخته ، ثم يتم التحقق من ذلك قبل التحديث مباشرة ، وإذا لم يتطابق معنا.
يعمل فحص الإصدار بشكل جيد لمتجر الذاكرة لأن لدينا قفلًا حصريًا للبيانات (يمكن للمتصل فقط تعديل الحالة في وقت واحد) ، ولكن سيحتاج إلى نهج مختلف لـ DB مناسب. ربما يمكننا تحديث مكان تطابق المعرف والإصدار ، حدد عدد السجلات المحدثة و balk إذا كان 0 (يعني أن الإصدار لم يتطابق ، أو أنه غير موجود).
تستخدم طبقة التخزين مخططًا للمعاملات البسيطة التي تتيح لمخازن البيانات المستقلة المشاركة في المعاملات. يتبع المستودع المركزي المعاملات النشطة ويتم استشارته عندما يتم جلب البيانات من متاجر البيانات للتأكد من أنها مستعدة للاستخدام. يضمن التزامن المتفائل على البيانات أن المعاملات النشطة المتعددة لا يمكن محاولة تعيين نفس القيمة في نفس الوقت. هذا ينتهك العزلة الحقيقية ، لكنه يبقي الأمور بسيطة ، ويتيح لنا تقليل الحالة اللازمة لكل قيمة يتم تخزينها.
حقن التبعية مفيد كممارسة للاعتماد عليها عند تصميم التطبيقات. يتيح لك فصل مخاوف دقة التبعية من منطق التطبيق. كما أنه يمنحك طريقة واضحة لتوسيع نطاق التطبيق. يعتمد هذا التطبيق نمطًا بسيطًا يعطينا هذه الفوائد دون الكثير من البنية التحتية.
لا يستخدم هذا التطبيق انعكاسًا لحاوية التحكم كما قد يتم استخدامه إذا كتبت تطبيقات .NET. هذا في الغالب لأنه لا يوجد حقًا أي شيء للصدأ. إنها مشكلة صعبة. إنه يستخدم نمط حقن التبعية البسيط لتأليف الأوامر والاستعلامات رغم ذلك ، حتى بدون حاوية متطورة.
الهدف الرئيسي لحقن التبعية هنا ليس دعم السخرية. إنها تقليل التعقيد عن طريق دفع المخاوف المحيطية بعيدًا عن منطق المكون الفردي.
تعيش المكونات المحقونة في الوحدة النمطية الخاصة بها. تحتوي تلك الوحدة على:
Resolver مشترك يحتوي على طريقة تُرجع التنفيذ الافتراضي دون الحاجة إلى تبعياتها.impl Trait . أنت لا تعرف أبدًا ما هو النوع الملموس الذي يستخدمه هذا التنفيذ الافتراضي.Arc ، Box . يبدو Resolver المشترك بعض الشيء ، وهو محمل في الخدمة ، وهو ، ولكن نظرًا لأن دقة التبعية موجودة بالكامل في كتل الصلح على Resolver نفسه ، فإننا نتجنب مسألة الاعتماد على Magic Global State في منطق التطبيق الخاص بنا.
لتقليل لوحة الغلاية ، بالنسبة للمكونات ذات طريقة واحدة فقط ، نقوم أيضًا بتنفيذها لسمات Fn . يتيح لك ذلك تجنب إعلان هيكل لهم عام على جميع تبعياتهم. سوف يهتم برنامج التحويل البرمجي للصدأ بذلك من أجلك.
من الصعب وصف هذا النمط في النثر ، تحتاج إلى رؤيته. ألقِ نظرة على وحدة domain/products/commands/create_product ، أو وحدات domain/products/model/store لأمثلة على نمط حقن التبعية هذا في العمل.
Resolver "كائن الله"؟ كائن الله " هو كائن في التطبيق الخاص بك يجمع كل المنطق المهم لدرجة أنه لا يمكنك العمل مع مكونات دون العمل أيضًا من خلال كائن الله. إنها مشكلة لأنها تصبح من الصعب بناء أو تغيير. نمط Resolver هنا هو كائن إله ، ولكنه ليس ضروريًا لبناء المكونات الفردية. يتعامل Resolver فقط مع مكون من التبعيات.