هذه هي المشاركة الأولى في سلسلة من المنشورات التي توضح فهمي لبنية تطبيق Flutter. أنا أحذرك - ستكون ثقة بالنفس .
المخطط حتى الآن:
مقدمة
لقد عملت في البرمجة منذ حوالي 20 عامًا. بدأت تطوير الهاتف المحمول منذ 4 سنوات مع Xamarin.Forms ، لأن النظام الأساسي كان الحافز الوحيد لي كمطور مستقل. Xamarin.Forms يدفعك حرفيًا إلى استخدام نمط MVVM ، نظرًا لتعريف واجهة المستخدم في XAML ، وتحتاج إلى نوع من الطبقة لإلصاق UI مع الطراز. في عملية العمل مع Xamarin ، قابلت ReactiveUI وكنت مفتونًا فعليًا بالتيارات والملحقات التفاعلية ( Rx ) التي جعلت من تطبيقي أكثر موثوقية.
بينما كانت Xamarin.Forms MVVMs خارج الصندوق ، عندما تحولت إلى Flutter ، فوجئت بعدم وجود أنماط تصميم مماثلة فيها. بدأت في البحث عن الأساليب المختلفة المقترحة ، لكن أيا منها لم يكن راضيا تماما عني:
- InheritedWidget : لم أستطع تحديث الجزء الذي تم تغييره فقط من شجرة القطع المصغّرة ، لذلك اعتدت استخدامه فقط للوصول إلى تيارات دارت نشر نماذج الفئات (Dart Streams) ، لكن سرعان ما تخلت عن هذه الفكرة لصالح القالب Service Locator
- يعد الطراز Scoped Model أكثر إثارة للاهتمام من
InheritedWidget
، لكنه لم يمنحني الكثير من المرونة كما اعتدت على ReactiveUI - كان Redux هو القالب الذي أوصى به العديد من المطورين على دراية React Native. لديّ وظيفة كاملة حول سبب عدم إعجابي بها.
- BLoC : إذا لم أكن قد بدأت بالفعل في تطوير نمطي الخاص في الوقت الذي بدأت فيه BLoC في التقدم ، فمن المرجح أن أكون قد اتخذت ذلك ، لأنه مرن فعليًا ورد الفعل. ما لا يعجبني هو أنه ينشر Stream Sinks ولا يمكنني فقط تولي المهام أو الأوامر وتمريرها إلى معالج أحداث عنصر واجهة المستخدم. بالإضافة إلى ذلك ، لا يخبرك BLoC بكيفية هيكلة تطبيقك ككل ، كما أنه لا يوجد تعريف واضح لمدى حجم BLoC أو نطاقه
- MVVM : منذ أن عملت معه ، هذا هو أول شيء أتمنى تنفيذه في Flutter. لكن لا! الهدف من ViewModel هو توفير تمثيل النموذج بأمان في طريقة العرض من خلال الارتباطات. لكن Flutter لا تقوم بتحديث طرزها ببيانات جديدة ، فهي تقوم دائمًا بإعادة بنائها ، كما وصفتها بالفعل . بالإضافة إلى ذلك ، يجب أن يكون تطبيق ViewModels متزامنًا دائمًا مع النموذج الأساسي ، مما يؤدي إلى حدوث أخطاء غير سارة ، ويظهر الواقع أن الميزة الموعودة المتمثلة في إعادة استخدام ViewModels في التطبيقات لم تتحقق أبدًا تقريبًا. آدم بيدلي لديه وظيفة كبيرة عن هذه العيوب
الحقيقة القاسية حول طبقة التكرار
إنها عقيدة تقريبًا فكرة أن عليك دائمًا إنشاء تطبيقك في عدة طبقات ، في كل منها حق الوصول إلى الأساس فقط ، لأن هذا سيسمح لك:
- إعادة استخدام الطبقات في مشاريع أخرى
- استبدال بشفافية طبقة واحدة مع آخر
- تبسيط الاختبار
ولكن:
- في ممارستي لم تكن هناك حالة لاحظت فيها إعادة استخدام كاملة للطبقات. إذا كان لديك رمز عالمي يمكن إعادة استخدامه ، فمن المنطقي أن تضعه في مكتبة عامة ؛
- استبدال طبقات كاملة هو أيضا ليست ممارسة شائعة. من غير المحتمل أن يستبدل معظم الأشخاص قاعدة البيانات بعد أن يصل التطبيق إلى مرحلة معينة من التطوير ، فلماذا تضيف طبقة تجريدية لذلك. حسنًا ، إذا كانت بعض أدوات التطوير الحالية مطلوبة ، فإن إعادة التجهيز سهلة للغاية ؛
- ما يعمل حقا هو تبسيط الاختبار
أنا لست ضد استخدام الطبقات ، ومع ذلك ، يجب أن نتبع هذه القاعدة بتهور كما كان من قبل. الاستخدام المفرط لها يؤدي إلى زيادة في التعليمات البرمجية ، ويمكن أن يخلق مشاكل مع الحفاظ على مصدر واحد لحالة التطبيق. لذلك ، قم بتطبيق الطبقات عندما يكون ذلك ضروريًا بالفعل ، وليس استنادًا إلى "أفضل الممارسات".
العمارة المثالية للرفرفة
فما الذي أتوقعه من الهندسة المعمارية المثالية؟
- سهولة فهم التطبيق. بالنسبة لي ، هذا هو الهدف الأكثر أهمية. يجب على المطورين الجدد المشاركين في العمل مع الكود الموجود فهم بنية التطوير بسهولة
- فريق تطوير سهولة
- يجب أن تكون الهندسة المعمارية نفسها سهلة الفهم والمحافظة عليها.
- لا رمز قالب للعكازات في التنمية
- رفرفة دعم أسلوب رد الفعل
- من السهل تصحيح الأخطاء
- الأداء لا ينبغي أن يعاني
- سهولة التوسع
- سهولة الاختبار
- فرص للتركيز على التطبيق بدلاً من التجول في المصدر
استقلال القطعة
بناءً على طبيعة عناصر الواجهة عديمي الجنسية ، لا يجب أن تعتمد أي صفحة / عنصر واجهة مستخدم في Flutter على الآخرين أو تؤثر عليهم. يؤدي هذا إلى فكرة أن كل صفحة / عنصر واجهة مستخدم يجب أن تكون مسؤولة بشكل مستقل عن عرض نفسها وعن تفاعلاتها مع المستخدم.
RxVMS
RxVMS هو تطور لنمط RxVAMS الموضح في منشور سابق ، خلال التطبيق العملي الذي تم فيه تحديد بعض المشكلات وحلها.
والنتيجة الحالية لكل هذه الأفكار هي نمط خدمات RxVMS أو Rx-View-Managers. ينفذ جميع المهام المذكورة أعلاه مع الشرط الوحيد الذي يجب أن نفهم Rx المواضيع والعناصر. لمساعدتك في ذلك ، أهدي المشاركة التالية.
فيما يلي نبذة مختصرة عن طلبي

الخدمات (الخدمات)
هذه عبارة عن مجموعة من واجهات التعامل مع العالم الخارجي ، والتي يمكن أن تعمل كقواعد بيانات ، واجهات برمجة تطبيقات REST ، إلخ. أنها لا تؤثر على حالة التطبيق.
مدراء (مدير)
يقوم المديرون بتجميع وظائف مماثلة بشكل شبه دلالة ، مثل المصادقة وإجراءات الطلب وما شابه. يعالج المديرون حالة الكائن.
يجب إجراء أي تغيير في الحالة (تغييرات في بيانات التطبيق) فقط من خلال المديرين. كقاعدة عامة ، لا يقوم المديرون أنفسهم بتخزين البيانات ، إلا في الحالات الحرجة بالنسبة لثوابت الأداء أو وقت التشغيل.
يمكن للمديرين أن يعملوا كمصادر لبيانات الوكيل ، في حالة الحاجة إلى تحويل معين بعد طلب من الخدمات. مثال على ذلك هو دمج البيانات من مصدرين أو أكثر للعرض في طريقة عرض.
يمكن للمديرين التفاعل مع بعضهم البعض.
المشاهدات (العرض، vyuha)
عادةً ما يكون هذا هو StatefullWidget أو StreamBuilder ، وهو قادر على استخدام البيانات من المديرين والخدمات. يمكن أن يكون صفحة كاملة أو القطعة. المشاهدات لا تخزن أي ولاية ويمكنها الاتصال مباشرة بالخدمات أثناء احترام هذه القاعدة.
كائنات المجال
على الرغم من عدم تضمينها في الرسم التخطيطي ، إلا أنها كيانات مهمة تمثل نموذج الأعمال. في الأنماط الأخرى ، يمكن أن تنتمي إلى طبقة منفصلة ( نموذج الأعمال ) بالتزامن مع منطق العمل. هنا ، في RxVMS ، لا تحتوي على أي منطق يغير حالة التطبيق. دائمًا ما تكون هذه هي أنواع بيانات بسيطة - كائنات بيانات عادية (إذا قمت بتضمينها في النموذج ، فستبدو مثل RxVMMS ، وهي طويلة بعض الشيء وتؤدي إلى الالتباس - قد يكون مخطئًا في VM باعتباره ViewModel). منطقيا ، وتقع كائنات المجال في طبقة المديرين.
الوظائف التالية سوف تكشف عن جوهر وتفاعل هذه العناصر.