وش يعني "تصميم الأنظمة" فعليًا لفلاتر؟

وش يعني "تصميم الأنظمة" فعليًا لفلاتر؟

خليني أقولك سرّ وسخ في مجتمع فلاتر: لما نسمع جملة "تصميم أنظمة" معظم المطورين يبدأون فورًا يرسمون مخططات load balancers و message queues. وأنا فاهمهم — هذا اللي علّمتنا إياه كتب المقابلات. لكن أول ما يُطلَب منك تصمّم "أنظمة" الفرونت-إند — شجرة الودجت، تدفق الحالة، طبقة البيانات في تطبيق فلاتر — تلاقي الصوت انقطع في القاعة.

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

أنا قضيت السنة الماضية أتفرج على وكلاء ذكاء اصطناعي يكتبون تطبيقات فلاتر بسرعة ترعب. بعض الكود كان جميل. لكن أغلب اللي شفته كان كومة ديون تقنية متماسكة بـ `StatefulWidget` واحد يلمس ثلاث APIs مختلفة. هذه ليست مشكلة أدوات. هذه مشكلة تفكير.

خلّنا نصلحها.

وش يعني "تصميم الأنظمة" فعليًا لفلاتر؟

لما مهندس Senior يسمع "صمّم تطبيق دردشة" ما يبدأ بـ `ListView.builder`. يبدأ بالحدود.

مهندس الباك-إند يفكر في خدمات وقواعد بيانات وطوابير. مهندس فلاتر لازم يفكر في:

- **تقسيم الودجتس** — متى يتحول ودجت من 600 سطر إلى مجلد فيه ودجتس مركّزة؟
- **ملكية الحالة** — شنو الحالة اللي تعيش في شجرة الودجت، وشنو اللي يعيش في controller، وشنو اللي يعيش في repository؟
- **تدفق البيانات** — وش هو الاتجاه الأحادي للبيانات من الشبكة إلى البكسل؟
- **معالجة الفشل** — وين تعيش retries و fallbacks وحالات الخطأ؟

مهندس فلاتر الكبير ينظر للتطبيق مثل البصلة. الطبقة الخارجية هي شجرة الودجت (العرض). الطبقة الوسطى هي إدارة الحالة (منطق التطبيق). القلب هو الوصول للبيانات (repositories, services, local storage). وظيفة تصميم الأنظمة هي الحفاظ على هذه الطبقات من أن تذوب في بعضها.

القاعدة اللي تحفظ عقلك: الودجتس ما تسوي أعمال

هنا قاعدة أعطيها لكل متدرب معي: **الودجتس حقك لازم ما تعرف منطق الأعمال، ومنطق الأعمال لازم ما يعرف الودجتس.**

إذا كان `TextFormField` ينادي repository مباشرة، كسرت الحدود. إذا كان الـ repository يعرف وش هو `TextEditingController`، كسرت الحدود بالاتجاه المعاكس. هذا الفصل هو اللي يخلي الكود قابل للاختبار والاستبدال — والأهم في عصر الذكاء الاصطناعي — يخلي الوكيل يقدر يعدّل الكود بدون ما يفجّر تطبيقك كامل.

كيف الذكاء الاصطناعي يغيّر اللعبة

أدوات مثل GitHub Copilot و Claude Code و Cursor غيّرت جذريًا طريقة كتابة الكود. أقدر أطلّع تطبيق CRUD كامل المواصفات في ظهرانية. هذا مو مبالغة؛ هذا يوم خميس عادي.

بس الشيء اللي تعلمته بعد عشرات مشاريع فلاتر المعتمدة على الذكاء الاصطناعي: **الوكلاء ممتازون في كتابة الكود وسيّئون جدًا في قرارات المعمارية.**

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

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

سيناريو واحد: الفوضى اللي يولدها الذكاء الاصطناعي

الشهر الماضي، مطور أتابعه فتح مشروعه وقالي شوف وش سوى الوكيل. المهمة كانت بسيطة: "أضف دعم العمل دون اتصال لشاشة الطلبات."

شنو سوى الوكيل؟

- أضاف `path_provider` و `sqflite` في ملف `pubspec.yaml`
- أنشأ قاعدة بيانات SQLite جوه `initState` حق ودجت
- لَفّ عميل HTTP حق طرف ثالث داخل class مخصص بدون أي interface
- خزّن استجابات JSON كاملة في متغير static عام

تقنيًا، التطبيق "اشتغل" — بس على المسار السعيد. لكن اتصال قاعدة البيانات تسرّب، والكاش ما عنده استراتيجية إبطال، والواجهة تجمّدت على الأجهزة الضعيفة لأن عمليات SQLite شغّلت على الـ main isolate.

العلاج مو "برومبتات أفضل". العلاج كان **تصميم أنظمة أفضل** — تقرر *قبل* ما الوكيل يكتب أي سطر أن طبقة التخزين راح تكون خلف repository مجرّد، وتستخدم تخزين آمن على مستوى الكومبايل مثل [drift](https://drift.simonbinder.eu)، وتكشف عن stream للحالة والواجهة تكتفي بمشاهدته.

أنماط تصميم أنظمة لازم كل مهندس فلاتر يعرفها

خلني أعطيك الخطة اللي أستخدمها لما يُطلب مني تصميم ميزة فلاتر — سواء كان الطلب من إنسان، أو وكيل، أو الاثنين.

1. الكعكة الثلاثية الطبقات (عرض / تطبيق / بيانات)

- **طبقة العرض**: ودجتس، أنيميشن، توجيه. تستهلك الحالة وتطلِق نوايا (intents).
- **طبقة التطبيق**: إدارة الحالة (Riverpod providers, Bloc cubits, ChangeNotifiers). تحول النوايا إلى تغييرات حالة.
- **طبقة البيانات**: repositories, services, قواعد البيانات المحلية, عملاء الشبكات. تتعامل مع زحمة الحديث مع العالم الخارجي.

كل طبقة تتحدث فقط مع الطبقة اللي تحتها مباشرة. الودجتس أبدًا ما تلمس `Dio`. الـ repositories أبدًا ما ترجّع `BuildContext`.

إذا تسأل "وين يعيش التحقق من صحة النموذج؟" أو "هل أضع مفتاح API داخل الـ Provider؟" — الجواب دايمًا: "في الطبقة اللي تملك هذا الاهتمام."

2. إدارة الحالة كتدفق أحادي الاتجاه

ما يهمني أي مكتبة إدارة حالة تختارها — [Riverpod](https://riverpod.dev) أو [Bloc](https://bloclibrary.dev) أو حتى `InheritedWidget` بالفانيليا — المهم يكون التدفق باتجاه واحد:

```
Stream of State -> Widgets Render -> User Intents -> State Mutations -> Repeat
```

لحظة ما يصير عندك two-way bindings، عندك بَقّ. لحظة ما الودجتس تعدّل الحالة مباشرة، عندك بَقّ ما تقدر تعيد إنتاجه. لحظة ما عندك `static var` عام يخزّن بيانات المستخدم، عندك مراجعة أمنية في مستقبلك.

3. حدود الخطأ والتراجع الرشيق

المهندس الكبير يصمم للفشل أولًا. كل شاشة تجاوب على الأسئلة:

- وش يصير لو الـ API واقع؟
- وش يصير لو ما عند المستخدم إنترنت؟
- وش يصير لو شكل الـ JSON تغيّر؟
- وش يصير لو البيانات null؟

هذا مو معناه نغلف كل شيء في try-catch. معناه نصمم *أنواع حالة* تتضمن `loading` و `loaded` و `error` و `empty` — ومن ثم نتأكد أن كل ودجت يعرف يرسّم الأربعة. أعدك أن هذا أنفع من أي package غامض.

منهج "المواصفة أولًا" لفلاتر مع الذكاء الاصطناعي

ذكرت أن وكلاء الذكاء الاصطناعي يعومون بدون قيود. الحل اللي اشتغل بشكل ثابت معي هو **التطوير الموجه بالمواصفة**. اكتب المواصفة *قبل* ما يبدأ الوكيل يبرمج.

هذا هو الـ workflow:

1. **اكتب صفحة وحدة باسم Architecture Decision Record (ADR)** للميزة. خلّها أقل من 500 كلمة. تضمّن هيكل شجرة الودجت، ونقاط ربط إدارة الحالة، وعقود البيانات.
2. **حدد حدود الملفات**. سمّي المجلدات: `features/checkout/widgets/`، `features/checkout/logic/`، `features/checkout/data/`. عط الوكيل مسارات ملفات صريحة.
3. **اطلب من الوكيل يسألك عن القرارات المعمارية، ولا ياخذها**. هذي الجملة الوحيدة تمنع 90% من قصص الرعب المعمارية الناتجة عن الذكاء الاصطناعي.
4. **راجع كود الوكيل مقابل الـ ADR قبل ما تشغّله**. مراجعة الكود لسه أفضل اختبار عندك.

هذا مو بيروقراطية. هذا تأكيد أن سطور الوكيل 500 تنزل في المكان الصحيح. لأن البديل هو إعادة البناء — وإعادة بناء كود ذكاء اصطناعي أسوأ من إعادة بناء كود بشري، لأن *ما أحد* كتبه بنيّة أصيلة.

سيناريو اثنين: بث استجابات الذكاء الاصطناعي (أيوه، أنت تبني ميزة ذكاء اصطناعي)

تخيل أنك تبني ميزة محادثة بالذكاء الاصطناعي. الوكيل يقدر يطلّع شاشة بث استجابات متحركة بشكل جميل في دقائق. بس تراجع خطوة واكتب على مستوى الأنظمة:

- وين تعيش سجل المحادثة؟ الذاكرة، تخزين محلي، سيرفر؟
- وش يصير لو انقطع بث HTTP في نص استجابة؟
- كيف تحدّ من المعدل وتتعامل مع 429؟
- كيف تتعامل مع محتوى غير آمن أو هلوسات في الواجهة؟

المهندس الكبير يصمم حالة البث (`StreamBuilder`، حالة مدعومة بـ buffer، منطق إعادة المحاولة) قبل ما يكتب أي ودجت. والباقي يقدر الوكيل يكمّله.

سيناريو ثلاثة: تطبيق مهام بدون اتصال Offline-First

صمّم تطبيق مستخدمين يديرون مهام وهم جوه طيارة. سؤال تصميم الأنظمة هو: هل تحسّن لعدم فقدان البيانات أبدًا، أو تحسّن للسرعة؟

مع قاعدة بيانات محلية مبنية على `drift`، و repository يزامن مع REST API، و connectivity provider يتتبع حالة الشبكة، التصميم يبقى نظيفًا. الوكيل يكتب الأنابيب؛ والمهندس الكبير يحدد *شكل* الأنابيب.

شنو الذكاء الاصطناعي يخلي "غير ضروري" (وشنو لا)

فيه موجة مقالات تدّعي أن الذكاء الاصطناعي بيُلغي الحاجة للمطورين البشر. الحالة المتفائلة تقول إنه يعطي المبدعين استقلالية أكبر؛ الحالة المتشائمة تقول إنه يكشف الفجوات اللي الاستقلالية تصعّب إخفاءها — Andy Budd كتب عن هذا في التصميم الرقمي، وهو ينطبق حرفيًا على فلاتر: الذكاء الاصطناعي يزيل *احتكاك* كتابة الكود لكن مو *مسؤولية* معرفة وش تكتب.

الذكاء الاصطناعي يقدر:

- يولّد كود ودجتس عام أسرع من كتابتك
- يترجم تصميم واجهة إلى شجرة ودجتس في ثواني
- يكتب unit tests للـ repositories

الذكاء الاصطناعي ما يقدر:

- يقرر وين تعيش الحالة
- يقرر المفاضلة بين نضارة الكاش وتكلفة الـ API
- يقرر شكل الـ skeleton حق التحميل لما يكون الـ API بطيء

تلك قرارات تصميمية. هي **وظيفتك**، وراح تظل وظيفتك بعد عشر سنين.

قائمة فحص سريعة لتصميم أنظمة فلاتر القادم

لما أحد يطلب منك تصمّم تطبيق فلاتر — في مقابلة، أو في مستند تصميم، أو حتى في رأسك — طبق هذه القائمة:

1. **وش هي الذرات الوظيفية؟** قسم التطبيق إلى ميزات. كل ميزة لازم تكون قابلة للبناء والاختبار والإزالة بشكل مستقل.
2. **وش هي ذرات الحالة؟** لكل ميزة حدد: وش الحالة المحلية، وش المشتركة، وش اللي ينحفظ؟
3. **كيف البيانات تدخل وتخرج؟** كل مصدر بيانات ياخذ repository. وكل repository يرجّع objects مكتوبة، مو JSON خام.
4. **وش يصير لما ينكسر؟** حالات فشل لكل استدعاء غير متزامن. حالات تحميل لكل stream.
5. **كيف مطور جديد (أو وكيل ذكاء اصطناعي) بيلاقي الكود؟** هيكل المجلدات يجب أن يشرح نفسه بنفسه.

لنكن واقعيين: مبدأ "جيد بما يكفي"

ما أبا أطلعك من هذا المقال وتفكر أن تطبيقك يحتاج ست طبقات تجريد قبل ما تضيف زر تسجيل دخول. تصميم الأنظمة له علاقة بالـ *تناسب*.

تطبيق أداة من شاشتين ما يحتاج Riverpod مولّد كود. يحتاج `StatefulWidget` و repository. تطبيق تجارة إلكترونية إنتاجي ما يحتاج ثلاثين provider عشان يرسم قائمة منتجات. يحتاج حدود واضحة، وملكية حالة مقصودة، وطبقة بيانات ما تتسرّب للواجهة.

المهندسون الكبار يعرفون متى *ما* يطبقون تعقيد. وكلاء الذكاء الاصطناعي ما يعرفون — بيسوّون لك Factory Pattern لميزة ممكن تكون أربعين سطرًا. وظيفتك أنك تكون الحاجز.

خواطر أخيرة

هذا اللي يفرّق مطور فلاتر مبتدئ عن Senior في عصر الذكاء الاصطناعي: **المبتدئ يطلب من الذكاء الاصطناعي يبني التطبيق، وبعدين يحاول يصلح اللي ينكسر. أما الـ Senior فيصمم حدود التطبيق، وبعدين يخلي الذكاء الاصطناعي يبني جواها.**

تبنّى عقلية الطبقات الثلاث. اكتب مواصفات قصيرة قبل ما تطلق الوكلاء. راجع بنيّة واضحة. صمم للفشل أولًا. وتذكر: النظام مربوط بـ *وش تبني*، مو بالذكاء الاصطناعي اللي يكتبه.

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

أسئلة شائعة

1. هل أحتاج فعلاً "تصميم أنظمة" لفلاتر؟ مو هذا شيء يخص الباك-إند؟

تصميم أنظمة الفرونت-إند حقيقي تمامًا. الموضوع يتعلق بتقرير وين تعيش الحالة، وكيف تتدفق البيانات، وفين تضع الحدود بين الودجتس والمنطق وطبقات البيانات. تصميم الباك-إند يعالج *الامتداد*؛ وتصميم الفرونت-إند يعالج *التعقيد*. تطبيق فلاتر فيه خمسين ودجت متشابك بلا حدود صعب صيانته تمامًا مثل باك-إند فيه خدمات متشابكة.

2. كيف أتعامل مع تصميم الأنظمة في مقابلة فلاتر؟

ركّز على فصل الاهتمامات. المحاورون يبون يسمعونك تتكلم عن تقسيم شجرة الودجت، وملكية الحالة (شنو محلي مقابل عام)، وعقود البيانات (شنو يرجّع الـ repository؟)، ومعالجة الفشل. ذكر أدوات مثل Riverpod أو Bloc جيد، لكن تسمية *متى تستخدم كل واحدة* أفضل. ولا تستعجل على الكود — تكلم عن المفاضلات أولًا.

3. هل أخلي وكلاء الذكاء الاصطناعي يصممون معمارية فلاتر من الصفر؟

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

4. هل Riverpod أو Bloc أفضل لتطبيقات فلاتر الكبيرة؟

الاثنان جاهزان للإنتاج؛ هذا سؤال فلسفي أكثر من سؤال تقني. Riverpod يعطيك أمان على مستوى الكومبايل، و providers بسياقات محصورة، وأسلوب وظيفي أكثر. Bloc يفرض نمط حدث/حالة صارم أسهل للتوثيق لكن على حساب الإسهاب. للتطبيقات الكبيرة، اختر بناءً على راحة فريقك وبعدين التزم به. تطبيق أي منهما باستمرار أفضل من التبديل في نص المشروع. اقرأ الوثائق — [riverpod.dev](https://riverpod.dev) و [bloclibrary.dev](https://bloclibrary.dev) — واتخذ القرار مع فريقك.

Comments (0)

No comments yet. Be the first to comment!

Leave a Comment