بنية الحل النقي ، الاختبارات دون الغوغاء وكيف جئت إلى هذا

مرحبا أيها القراء الأعزاء! في هذه المقالة ، أود أن أتحدث عن بنية مشروعي ، والتي أعيد تشكيلها أربع مرات عند إطلاقها ، حيث لم أكن راضيًا عن النتيجة. سأتحدث عن عيوب المناهج الشعبية وأظهر مصلحي.


أريد أن أقول على الفور أن هذا هو مقالي الأول ، وأنا لا أقول ما يجب القيام به مثلي - أليس كذلك. أريد فقط أن أظهر ما فعلته ، وأقول كيف وصلت إلى النتيجة النهائية ، والأهم من ذلك - الحصول على آراء الآخرين.


عملت في عدة حملات ورأيت مجموعة من كل شيء كنت سأفعله بطريقة مختلفة.


على سبيل المثال ، أرى غالبًا بنية N-Layer ، هناك طبقة للعمل مع البيانات (DA) ، وهناك طبقة ذات منطق عمل (BL) تعمل باستخدام DA وربما بعض الخدمات الأخرى ، وهناك أيضًا طبقة عرض \ API تم تلقي طلب تمت معالجته باستخدام BL. يبدو مريحًا ، لكن بالنظر إلى الرمز ، أرى هذا الموقف:


  • [DA] تسحب \ تكتب \ تغير البيانات ، حتى لو كان استعلام معقد - موافق
  • [BL] 80 ٪ يدعو 1 طريقة والقوائم النتيجة أعلاه - لماذا هذه الطبقة فارغة؟
  • [عرض] 80 ٪ Calls 1 BL طريقة إرم النتيجة أعلاه - لماذا هذه الطبقة فارغة؟

بالإضافة إلى ذلك ، من المألوف الالتفاف في الواجهات بحيث يمكنك فيما بعد قفل واختبار - واو ، واو!


  • لماذا تبلل؟
  • حسنا ، لخفض الآثار الجانبية لمدة الاختبارات.
  • هذا هو ، سنحتج دون آثار جانبية ، ولكن في همز معهم؟
    ...

هذا شيء أساسي لم يعجبني في هذه البنية ، لأن حل مشكلة مثل: "قائمة أمثال المستخدم" هي عملية كبيرة ، ولكن في الواقع هناك استعلام واحد في قاعدة البيانات وربما يتم تعيينه.


حل عينة

1) [DA] إضافة طلب إلى DA
2) [BL] استجابة DA إلى الأمام
3) [عرض] نتيجة BA إلى الأمام ، قد تعزز


لا تنس أن جميع هذه الأساليب لا تزال بحاجة إلى إضافتها إلى الواجهة ، فنحن نكتب مشروعًا من أجل التبليل ، وليس للحصول على حل.


في مكان آخر ، رأيت تطبيق API بنهج CQRS.


لم يبدو الحل سيئًا ، ميزة 1 مجلد - 1. يوجد مطور يصنع ميزة في مجلده ، ويمكن أن ينسى دائمًا تأثير شفرته على الميزات الأخرى ، لكن كان هناك العديد من الملفات لدرجة أنه كان مجرد كابوس. طلب / نماذج الاستجابة ، المدققون ، المساعدون ، المنطق نفسه. البحث في الاستوديو رفض عمليا ، وتم وضع الإضافات لإيجاد الأشياء الضرورية في الكود.


هناك الكثير مما يمكن قوله ، لكنني أبرزت الأسباب الرئيسية التي دفعتني إلى رفض ذلك


وأخيرا لمشروعي


كما قلت ، أعيدت بناء مشروعي عدة مرات ، في تلك اللحظة كان لدي "اكتئاب مبرمج" ، لم أكن راضيًا عن الكود الخاص بي ، وأعدته ، مرارًا وتكرارًا ، في النهاية ، بدأت أشاهد مقطع فيديو حول بنية التطبيق لمعرفة كيف البعض الآخر يفعل. صادفت تقارير أنطون مولدوفان عن DDD والبرمجة الوظيفية ، وفكرت: "من هنا ، أحتاج إلى F #!".


بعد قضاء يومين على F # ، أدركت ، من حيث المبدأ ، أن أفعل نفس الشيء في C # وليس أسوأ. أظهر الفيديو:


  • هنا هو رمز C # ، هو القرف
  • هنا هو F # بارد ، كتب أقل - فائقة.

لكن الحيلة هي أن الحل على F # تم تنفيذه بشكل مختلف ، وفي مقابل ذلك أظهروا تطبيقًا سيئًا على C #. كان المبدأ الرئيسي هو أن BL ليس شيئًا يستدعي خدمات DA ويقوم بكل العمل ، لكنه وظيفة محض .


بالطبع F # أمر جيد ، لقد أحببت بعض الميزات ، لكن مثل C # ، هذه مجرد أداة يمكن استخدامها بطرق مختلفة.


وعدت إلى C # وبدأت إنشاء.


أنا خلقت هذه المشاريع في الحل:


  1. API
  2. جوهر
  3. الخدمات
  4. اختبارات

أنا أيضا استخدام ميزات C # 8 ، وخاصة نوع refence nullable ، وسوف تظهر تطبيقه.
باختصار حول مهام الطبقات التي أعطيتها.


API
1) تلقي الطلبات ، طلب نماذج + التحقق من الصحة ، والقيود


مزيد من التفاصيل

صورة


2) استدعاء وظائف من الخدمات الأساسية


مزيد من التفاصيل

صورة


هنا نرى رمزًا بسيطًا قابلاً للقراءة ، وأعتقد أن الجميع سوف يفهمون ما هو مكتوب هنا.
نمط واضح لوحظ
1) الحصول على البيانات
2) العملية ، تعديل ، وما إلى ذلك - هذا الجزء يحتاج إلى اختبار.
3) حفظ.


3) رسم الخرائط ، إذا لزم الأمر
4) خطأ في معالجة (تسجيل + استجابة الإنسان)


مزيد من التفاصيل

تحتوي هذه الفئة على جميع أخطاء التطبيق الممكنة التي يستجيب لها معالج الاستثناء.


صورة


صورة


اتضح أن التطبيق إما يعمل ، أو يعطي خطأ محددًا ، وليست الأخطاء التي تمت معالجتها إما من الآثار الجانبية أو الأخطاء ، فإن سجل هذه الأخطاء ينتقل إلي فورًا في برقية في محادثة مع الروبوت.


لدي AppError.Bug هذا الخطأ لحالة غير واضحة.


لدي CallBack من خدمة أخرى ، سيكون لديك userId في نظامي ، وإذا لم أجد مستخدمًا يحمل هذا المعرف ، إما حدث شيء للمستخدم أو أنه غير واضح على الإطلاق ، مثل هذا الخطأ ينتقل إلي مثل CRITICAL ، نظريًا أن تنشأ ، ولكن إذا حدث ذلك ، فإنه يتطلب تدخلي.


صورة


الأساسية ، والأكثر إثارة للاهتمام


كنت أضع في اعتباري دائمًا أن BLs هي مجرد وظائف تعطي النتيجة نفسها بنفس المدخلات. كان تعقيد الكود في هذه الطبقة على مستوى العمل المختبري ، وليس وظائف كبيرة تؤدي وظائفها بوضوح ودون أخطاء. وكان من المهم عدم وجود آثار جانبية داخل الوظائف ، وكل ما كانت الوظيفة المطلوبة هي معاملته.


إذا كانت الوظيفة تحتاج إلى توازن مستخدم ، فسنحصل على التوازن ، وننقله إلى الوظيفة ، ولا ندفع خدمة المستخدم إلى BL.


1) الإجراءات الأساسية للكيانات


مزيد من التفاصيل

صورة
صورة


لقد توصلت إلى طرق كطريقة ملحق حتى لا يتم تنشيط الفئة ، ويمكن تجميع الوظيفة حسب الميزات.


صورة
صورة


أنا أعتبر البناء الجيد لنماذج الكيانات موضوعًا مهمًا بنفس القدر.


على سبيل المثال ، لدي مستخدم ، لدى المستخدم أرصدة بعدة عملات. أحد القرارات المعتادة التي اتخذتها دون تردد هو جوهر "الرصيد" ووضع مجموعة من الأرصدة في المستخدم. ولكن أي نوع من الراحة جلبت مثل هذا القرار؟


1) إضافة / إزالة العملات. تعني هذه المهمة على الفور ، ليس فقط كتابة رمز جديد ، ولكن أيضًا الترحيل ، مع ملء / حذف جميع المستخدمين الحاليين ، وهذا هو الخيار الأسهل. لا سمح الله ، لإضافة عملة جديدة ، سيكون عليك إنشاء زر للمستخدم ، والذي يقوم بالنقر فوقه ويبدأ إنشاء محفظة جديدة لبعض أنواع العمليات التجارية. ونتيجة لذلك ، كان من الضروري فقط توسيع التعداد للعملة الجديدة ، وكتبوا ميزة أخرى لإنشاء محافظ عن طريق زر ، وألقوا مهمة أخرى في المقدمة.


2) في الكود ، ثوابت FirstOrDefault (s => s.Currency == currency) والتحقق من وجود قيمة خالية


قراري

صورة


من خلال النموذج نفسه ، أضمن لنفسي أنه لن يكون هناك توازن فارغ ، ومن خلال إنشاء مشغل الفهرسة ، قمت بتبسيط الكود في جميع أماكن التفاعل مع الرصيد.


الخدمات


توفر لي هذه الطبقة أدوات ملائمة للعمل مع مختلف الخدمات.
في مشروعي أستخدم MongoDB ولعمل مريح معه ، قمت بلف المجموعات في هذا المستودع.


مزيد من التفاصيل

مستودع نفسه
صورة


يقوم Monga بحظر المستند في وقت العمل به ، على التوالي ، وهذا سيساعدنا في حل المشكلات في منافسة الطلبات. وفي mong هناك طرق للبحث عن كيان + يتصرف بناءً عليه ، على سبيل المثال: "ابحث عن مستخدم ذي معرف وأضف 10 إلى رصيده الحالي"


والآن حول ميزة C # 8.


صورة


صورة


يخبرني توقيع الطريقة أنه يمكن للمستخدم العودة ، وربما Null ، على التوالي ، عندما أرى المستخدم؟ أحصل على الفور على تحذير برنامج التحويل البرمجي ، وقم بفحص لاغٍ.


صورة


عندما ترجع الطريقة المستخدم ، أعمل معها بثقة.


صورة


أريد أيضًا أن ألفت الانتباه إلى حقيقة أنه لا يوجد أي محاولة للتجربة لأن الاستثناءات يمكن أن تكون فقط من "مواقف غريبة" وبيانات غير صحيحة لا ينبغي أن تصل إلى هنا لأن هناك التحقق من الصحة. لا يوجد أي محاولة للتجول في طبقة واجهة برمجة التطبيقات ، فهناك معالج استثناء عمومي واحد فقط.


هناك طريقة واحدة فقط يتم طرح استثناء هي طريقة التحديث.
انها تنفذ الحماية ضد فقدان البيانات في وضع متعدد الخيوط.
صورة


لماذا لم تستخدم طرق monga المذكورة أعلاه؟
هناك أماكن ما زلت لا أعرفها بشكل مؤكد ما إذا كان يمكنني حتى العمل مع المستخدم ، فربما لا يملك مالًا مقابل هذا الإجراء ، لذلك في البداية أخرج المستخدم وأراجعه ، ثم أطفأه واحفظه.


صورة


سيغير تطبيقي نظريًا رصيد المستخدم أكثر من مرة واحدة في الثانية ، حيث ستكون هذه ألعاب سريعة.


لكن نموذج المستخدم نفسه ، من الواضح أن إحالة المستخدم اختيارية ، ويمكنك العمل مع كل شيء آخر دون التفكير في أي شيء.


صورة


أخيرا الاختبارات


كما قلت ، تحتاج فقط إلى اختبار المنطق ، ومنطق وظيفتنا هو دون آثار جانبية.
لذلك ، يمكننا إجراء اختباراتنا بسرعة كبيرة ومع معايير مختلفة.


مزيد من التفاصيل

قمت بتنزيل nuget FSCheck الذي يولد بيانات واردة بشكل عشوائي ويسمح للعديد من الحالات المختلفة.


أحتاج فقط إلى إنشاء مستخدمين مختلفين وإطعام اختبارهم والتحقق من التغييرات.


يوجد منشئ صغير لإنشاء مثل هؤلاء المستخدمين ، لكن من السهل التوسع فيه.


صورة


وهنا هي الاختبارات نفسها


صورة


صورة


صورة


بعد إجراء بعض التغييرات ، أجري الاختبارات ، وبعد 1-2 ثانية أرى أن كل شيء على ما يرام.
كما أنه في الخطط لكتابة اختبارات E2E من أجل التحقق من واجهة برمجة التطبيقات (API) بأكملها من الخارج وتأكد من أنها تعمل كما ينبغي ، من الطلب إلى الاستجابة.


رقائق


أشياء رائعة قد تحتاجها

يتم فحص كل طلب من طلباتاتي ، وعندما يحدث خطأ ، أجد requestId ويمكنه بسهولة إعادة إنشاء الخطأ من خلال تكرار الطلب ، لأن API ليس لديه حالة ، ولكل طلب يعتمد فقط على معلمات الطلب.


صورة


لتلخيص.


لقد كتبنا حلاً حقًا ، وليس إطارًا يحتوي على مجموعة من التجريدات الإضافية ، وكذلك mok. لقد تعاملنا مع الخطأ في مكان واحد ونادرا ما يجب أن تحدث. فصلنا بين BL والآثار الجانبية ، والآن BL هو مجرد منطق محلي يمكن إعادة استخدامه. لم نكتب وظائف إضافية تقوم ببساطة بإعادة توجيه الدعوة إلى وظائف أخرى. سوف أقرأ التعليقات بنشاط وأكمل المقال ، شكرًا!

Source: https://habr.com/ru/post/ar459394/


All Articles