العودة إلى المدونةengineering6 دقيقة قراءة
البنية النظيفة في Flutter باستخدام BLoC: دليل عملي
كيف تُبقي clean architecture وBLoC تطبيقات Flutter قابلة للاختبار والصيانة ومنخفضة تكلفة التغيير مع نموها بعد الإصدار الأول.
Mazen Salah

بعد ستة أشهر من العمل على مشروع Flutter، يصلك طلب تعديل يبدو «بسيطًا»: تغيير مزوّد الدفع، أو إضافة مصدر بيانات ثانٍ، أو دعم سوق جديد بقواعد عمل مختلفة. في تطبيق مبني بإتقان، هذا عمل يستغرق بعد ظهيرة واحدة ويلمس ملفًا أو ملفين فقط. أما في تطبيق متشابك، فهو حفريات أثرية تمتد أسبوعين بين عناصر واجهة (widgets) تجلب البيانات من الـ API وتحلّل الـ JSON وتحمل منطق العمل وتدير الحالة كلها في آنٍ واحد. الفارق بين النتيجتين نادرًا ما يكون في إطار العمل، بل في البنية المعمارية (architecture).
الـ clean architecture في Flutter، مقترنةً بـ BLoC لإدارة الحالة (state management)، هي البنية التي نلجأ إليها حين يحتاج التطبيق أن يعيش أطول من سبرنت واحد وأن يصمد أمام انتقاله بين أكثر من مطوّر. هذه ليست طقوسًا أكاديمية، بل مجموعة من الحدود الفاصلة التي تُبقي تكلفة التغيير منخفضة.
ماذا تعني clean architecture هنا فعليًا
الـ clean architecture هي طريقة لتنظيم الكود بحيث تُفصل الأجزاء كثيرة التغيّر عن الأجزاء التي يجب أن تظل ثابتة. في تطبيق Flutter، يُترجَم هذا إلى ثلاث طبقات مع قاعدة صارمة حول الاتجاه المسموح به لتبعية كل طبقة على الأخرى.
- طبقة العرض (Presentation) — عناصر الواجهة والشاشات والـ BLoC الذي يحرّكها. هذه الطبقة تعرف Flutter والواجهة، ولا تعرف شيئًا عن كيفية تخزين البيانات أو جلبها.
- طبقة المجال (Domain) — منطق العمل الخالص: الكيانات (entities) وحالات الاستخدام (use cases) وعقود المستودعات (repository contracts). هذه هي قلب التطبيق، ولا تعتمد إطلاقًا على Flutter أو HTTP أو أي قاعدة بيانات. يمكنك تشغيلها داخل أداة Dart سطرية بسيطة.
- طبقة البيانات (Data) — التنفيذ الفعلي: عملاء الـ API وقواعد البيانات المحلية ونماذج الـ JSON والمستودعات الملموسة التي تفي بالعقود التي حدّدتها طبقة المجال.
أهم قاعدة على الإطلاق هي أن التبعيات تتجه إلى الداخل. العرض يعتمد على المجال، والبيانات تعتمد على المجال، والمجال لا يعتمد على شيء. هذا ما يتيح لك استبدال API بخادم خلفي مختلف، أو تغيير ذاكرة التخزين المحلية، دون أن تشعر قواعد العمل أو الواجهة بأي شيء.
في تطبيق توصيل، قد تُعرّف طبقة المجال حالة استخدام PlaceOrder وواجهة OrderRepository. أما طبقة البيانات فهي التي تقرر إن كان الطلب يذهب إلى REST API، أو إلى مجموعة في Firebase، أو إلى طابور SQLite محلي. وطبقة العرض تكتفي باستدعاء حالة الاستخدام. لا تحتاج أي منها لمعرفة تفاصيل تنفيذ الأخرى.
أين يقع BLoC ولماذا ينجح
الـ BLoC (اختصارًا لـ Business Logic Component) هو نمط لإدارة الحالة مبني على دورة بسيطة ومتوقعة: ترسل الواجهة أحداثًا (events)، يعالجها الـ BLoC ويُصدر حالات (states)، ثم تعيد الواجهة بناء نفسها وفق الحالة التي تستقبلها. لا شيء يغيّر الشاشة سوى حالة جديدة، وهذا يجعل سلوك التطبيق سهل الفهم وسهل الاختبار.
داخل الـ clean architecture، يقع الـ BLoC في طبقة العرض لكنه يبقى نحيلًا عن قصد. لا يستدعي الـ API ولا يلمس قواعد البيانات مباشرة، بل يستدعي حالة استخدام من طبقة المجال ويترجم النتيجة إلى حالات مثل Loading أو Loaded أو Error.
يبدو التدفق المعتاد لتحميل كتالوج منتجات على النحو التالي:
- ترسل الشاشة حدث
LoadProductsعند فتحها. - يُصدر الـ BLoC حالة
ProductsLoadingويستدعي حالة الاستخدامGetProducts. - تطلب حالة الاستخدام البيانات من المستودع، والمستودع يقرر بين الذاكرة المؤقتة والشبكة.
- يستقبل الـ BLoC النتيجة ويُصدر
ProductsLoadedمع القائمة، أوProductsErrorمع رسالة. - تعيد الواجهة البناء لكل حالة باستخدام
BlocBuilder.
هذا الفصل هو ما يجعل إدارة الحالة عبر BLoC قابلة للتوسّع. ولأن الـ BLoC يعتمد على حالة استخدام فقط، يمكنك اختباره بحالة استخدام وهمية دون لمس شبكة حقيقية. ولأن الواجهة تعتمد على الحالات فقط، يمكنك إعادة تصميم الشاشة دون لمس أي منطق. كل جزء يتغيّر بشكل مستقل.
ملاحظة حول الـ Cubit
لست بحاجة دائمًا إلى آلية BLoC الكاملة. الـ Cubit، الأخ الأخف في الحزمة نفسها، يُسقِط طبقة الأحداث ويتيح لك استدعاء الدوال مباشرة. للشاشات البسيطة، يُبقي الـ Cubit الأمور نظيفة دون تعقيد. تظل البنية المعمارية كما هي تمامًا، والأبسط هو آلية طبقة العرض فقط. الفريق العملي يمزج بينهما: Cubit للحالات المباشرة، وBLoC الكامل حيث تبرّر تدفقات الأحداث المعقّدة استخدامه.
الجدوى التجارية لهذه البنية
هنا تتوقف البنية المعمارية عن كونها تفضيلًا هندسيًا لتصبح قرارًا تجاريًا. بالنسبة لصاحب العمل أو مدير المنتج، تردّ الـ clean architecture مع BLoC تكلفتها بطرق تظهر في خارطة الطريق والميزانية معًا.
- تعديلات أرخص. حين يعيش منطق العمل في طبقة المجال معزولًا عن الواجهة والشبكة، يصبح تعديله محصورًا ومنخفض المخاطر. الأسواق الجديدة والتسعير الجديد والمزوّدون الجدد لا يتسببون في موجات تغيير تجتاح الكود بأكمله.
- قابلية اختبار حقيقية. لأن طبقة المجال خالية من تبعيات إطار العمل، يمكن اختبار منطقها بسرعة وعمق. تُكتشف الأخطاء قبل أن يراها المستخدمون، لا بعد تقييم بنجمة واحدة.
- فرق تعمل بالتوازي. يمكن لمطوّر أن يبني الواجهة اعتمادًا على الحالات المحددة، بينما يبني آخر طبقة البيانات خلف عقد المستودع. يلتقيان عند الواجهة البرمجية، لا في تعارضات الدمج.
- تسليم أكثر أمانًا. حين ينتقل المشروع إلى مطوّر جديد أو فريق داخلي، تعني البنية المتسقة أن بإمكانهم العثور على الأشياء والمساهمة خلال أيام، لا أسابيع. لا يصبح التطبيق رهينةً لمن كتبه أول مرة.
التكلفة حقيقية أيضًا، ونحن صادقون بشأنها. الـ clean architecture تضيف ملفات وطبقات وساطة. وبالنسبة لنموذج أولي من شاشتين هدفه التحقق من فكرة في عطلة نهاية الأسبوع، فهي مبالغة. لكن البنية تستحق كلفتها حين يُراد للتطبيق أن يدوم وينمو ويحمل عملًا تجاريًا.
تجنّب الأخطاء الشائعة
الفرق التي تتبنى الـ clean architecture وBLoC تتعثّر عادةً في النقاط نفسها.
- المبالغة في التطبيق على كل شيء. ليست كل شاشة بحاجة إلى حالة استخدام ومستودع وثلاثة نماذج. طبّق البنية الكاملة على المزايا ذات منطق العمل الحقيقي، وأبقِ الشاشات التافهة خفيفة.
- BLoC متضخّم. حين يبدأ الـ BLoC في تحليل الـ JSON أو بناء الاستعلامات، فهذا يعني أن الحدّ الفاصل قد تسرّب. ادفع هذا العمل إلى طبقة البيانات حيث ينتمي.
- مجال فارغ. إن كانت كياناتك مجرد حاويات بيانات وكل المنطق يعيش في الـ BLoCs، فأنت تملك المجلدات دون الفائدة. المجال يجب أن يمتلك القواعد.
- إرهاق التحويل (mapping). نعم، ستحوّل نماذج الـ API إلى كيانات مجال. هذا التحويل هو الدرز الذي يحميك حين يتغيّر الـ API. إنه ميزة، لا هدر.
أهم النقاط
- الـ clean architecture تقسّم تطبيق Flutter إلى طبقات عرض ومجال وبيانات، مع توجيه كل التبعيات إلى الداخل نحو مجال خالٍ من إطار العمل.
- يحرّك BLoC الواجهة عبر دورة واضحة من الحدث إلى الحالة، ويبقى نحيلًا مفوّضًا العمل الحقيقي لحالات استخدام المجال.
- تجعل البنية التعديلات أرخص، والمنطق قابلًا للاختبار فعليًا، والفرق قادرة على العمل بالتوازي والتسليم بأمان.
- استخدم Cubit للحالات البسيطة وBLoC الكامل لتدفقات الأحداث المعقّدة، وتبقى البنية كما هي في الحالتين.
- طبّق النمط الكامل على المزايا ذات منطق العمل الحقيقي، لا على النماذج الأولية العابرة حيث يضيف عبئًا فقط.
إن كنت تخطط لتطبيق Flutter يحتاج أن ينمو بعد إصداره الأول دون أن ينهار تحت وزنه، فإن قرارات البنية المعمارية التي تتخذها الآن هي التي ستحدد سرعة تحرّكك لاحقًا. تصفّح خدماتنا وأعمالنا لترى كيف نبني تطبيقات تبقى قابلة للصيانة، ثم تواصل معنا. سنساعدك على تصميم بنية clean architecture وهيكل BLoC يناسب منتجك وفريقك وخارطة طريقك.
عن الكاتب
Mazen Salah
Founder & Lead Engineer
Mazen Salah founded SummationWorks in 2019 to help startups and growing businesses ship real software. He leads engineering across the company's web, mobile, and AI work, building products with Next.js, Flutter, Laravel, and Node.
المزيد عنّامقالات ذات صلة

engineering5 دقيقة قراءة
أودو والمرحلة الثانية من الفوترة الإلكترونية: الامتثال لهيئة الزكاة والضريبة والجمارك
أُغلقت الموجة 24 في يونيو 2026، والموجة 25 تشمل كل منشأة تتجاوز إيراداتها 187,500 ريال. كيف تعمل مرحلة الربط والتكامل، وما الذي يوفّره أودو أصلاً، وخطوات الإعداد التي تتعثّر فيها معظم الشركات.
Mazen Salah

engineering5 دقيقة قراءة
وحدات أودو المخصّصة: متى تُعِدّ ومتى تخصّص وكيف تُبقي الترقيات سهلة
أودو سهل التخصيص — وهذا تحديداً ما يوقع الشركات في أنظمة يستحيل صيانتها. قواعد القرار، وأنماط الوحدات، والعادات التي تُبقي أودو المخصّص قابلاً للترقية.
Mazen Salah

engineering4 دقيقة قراءة
أودو 19 اليوم وأودو 20 هذا الخريف: متى تُرقّي وكيف
أدخل أودو 19 الذكاء الاصطناعي في كل تطبيق مع واجهة جديدة؛ ويصل أودو 20 نحو أكتوبر 2026. دليل عملي لما تغيّر، والإصدارات التي ينتهي دعمها، وكيف تخطّط لترقية لا تكسر تخصيصاتك.
Mazen Salah
لديك مشروع في ذهنك؟
لنحوّل فكرتك إلى برمجيات جاهزة للإنتاج.