معظم النصائح والمقالات حول “الهندسة المعمارية لـ Flutter” تتلخص إما في قالب جاهز يضم 40 ملفًا فارغًا، أو مجرد كلام عام غير مفيد. بعد سنوات من إطلاق تطبيقات حقيقية، إليك الخلاصة العملية التي تمنيت لو نصحني بها أحدهم في بداياتي.
ركز على الطبقات، وليس على المجلدات
ترتيب المجلدات ليس مهمًا. ما يهم حقًا هو اتجاه الاعتماديات (Dependencies). حافظ دائمًا على ثلاث طبقات أساسية وتأكد من أن الاعتماديات تتجه نحو الداخل فقط:
- طبقة العرض (Presentation) — هنا تعيش الـ Widgets، إدارة الحالة (State)، والتنقل (Navigation).
- طبقة النطاق (Domain) — هنا يوجد الكيان البرمجي (Entities) وحالات الاستخدام (Use Cases). يجب ألا تحتوي هذه الطبقة على أي استيراد (import) لمكتبات Flutter. أبدًا.
- طبقة البيانات (Data) — هنا تتواجد الـ Repositories، التعامل مع الـ APIs، والتخزين المحلي.
حالة الاستخدام (Use-case) هي في النهاية مجرد دالة (Function). قاوم رغبتك في تحويلها إلى صنف (Class) معقد مكون من 200 سطر:
class GetActiveRides { GetActiveRides(this._repo); final RideRepository _repo;
Future<List<Ride>> call() => _repo.activeRides();}اجعل إدارة الحالة (State Management) مملة وواضحة
سواء كنت تستخدم Riverpod أو Bloc أو Signals، القاعدة الذهبية واحدة: يجب أن تكون الحالة مُشتقّة، والتأثيرات الجانبية (Side Effects) صريحة. إذا احتجت لقراءة ملف كامل لتعرف أين يحدث تأثير جانبي معين، فتجريدك (Abstraction) غير صحيح.
final activeRidesProvider = FutureProvider.autoDispose((ref) { return ref.watch(getActiveRidesProvider)();});تجريدات بسيطة ذات قيمة عالية
- استخدم نوع
Result<T>بدلاً من رمي الأخطاء (Exceptions) بشكل عشوائي بين الطبقات. - اعتمد هيكلًا موحدًا مثل
AppExceptionلالتقاط الأخطاء ومعالجتها على حدود طبقة البيانات. - خصص مكانًا واحدًا فقط لتحويل استجابات الـ API (Models) إلى كيانات النطاق (Domain Entities).
هذا كل ما في الأمر. الهندسة المعمارية في النهاية هي مجموعة القرارات التي ستكلفك الكثير إذا قررت تغييرها لاحقًا — استثمر جهدك وانضباطك فيها، وكن مرنًا في كل ما دون ذلك.