يا الله شو بذّكر هذيك الأيام… كنا شغالين على مشروع لمنصة تجارة إلكترونية ضخمة، فريق الواجهة الأمامية كان مكون من حوالي 20 مطور. التطبيق كان عبارة عن كتلة واحدة (Monolith)، قطعة كود عملاقة مكتوبة بـ React. في البداية، كانت الأمور “عال العال”، لكن مع كل ميزة جديدة، كانت الأمور تتعقد أكثر. كان بناء المشروع (build) ياخذ حوالي 15 دقيقة! يعني بتكبس الأمر، وبتروح بتعمل فنجان قهوة، وبتحكي مع الوالدة، وبترجع ولسه العداد بلف. أما المصيبة الكبرى فكانت لما يتداخل شغل فريقين على نفس الملفات… يا ويلي! كنا نقضي ساعات بس نحل تعارضات (conflicts) بسيطة. في اجتماع من الاجتماعات، واحد من الشباب الجداد اقترح فكرة تقسيم الواجهة الأمامية لتطبيقات أصغر. يومها، كثير من القدامى استهجنوا الفكرة وقالوا “شو هالحكي الفاضي؟ بدنا نعقد حالنا زيادة؟”. لكن أنا، أبو عمر، لمعت الفكرة في بالي. حسيت إنها زي اللي بده يبني عمارة ضخمة، ما بجيب كل العمال يشتغلوا على نفس الحيط، بقسمهم طوابق وشقق. ومن يومها، بدأت رحلتي مع عالم الواجهات المصغرة (Micro-Frontends)، رحلة مليئة بالتحديات والحلول المبتكرة اللي حابب أشارككم فيها اليوم.
الواجهات المصغرة (Micro-Frontends): تقسيم الغنيمة لتسهيل المهمة
تخيل أن تطبيقك ليس برنامجاً واحداً، بل هو عبارة عن “مول تجاري” كبير. كل متجر في هذا المول هو تطبيق صغير مستقل بذاته، له فريقه الخاص، وتقنياته الخاصة، ودورة حياة خاصة به. هذا هو جوهر بنية الواجهات المصغرة.
- متجر المنتجات: فريق مختص بـ React يدير صفحة عرض المنتجات.
- عربة التسوق: فريق آخر يستخدم Vue.js لتطوير تجربة الدفع.
- حساب المستخدم: فريق ثالث يستخدم Angular لإدارة ملفات المستخدمين.
هذا التقسيم يمنح الفرق استقلالية هائلة، ويسرّع وتيرة التطوير بشكل لا يصدق. لكن كما يقولون “كل وردة فيها شوك”. هذه البنية خلقت تحديات جديدة: كيف لهذه المتاجر المستقلة أن تتحدث مع بعضها؟ وكيف نضمن ألا يقوم كل متجر بتحميل مكتبة React الخاصة به، فننتهي بتحميل نفس المكتبة 5 مرات ونقتل أداء الموقع؟ هنا يأتي دور المنقذ.
Module Federation: اللاصق السحري الذي يجمع القطع
لو كانت الواجهات المصغرة هي قطع الليغو، فإن Module Federation هو اللوح الأساسي الذي يسمح لك بتثبيت هذه القطع معًا لتكوين شكل متكامل. ببساطة، هي تقنية تسمح لتطبيقات JavaScript منفصلة تمامًا بمشاركة الكود والاعتماديات (Dependencies) فيما بينها في وقت التشغيل (Runtime) وليس فقط وقت البناء.
تحديثات Module Federation 2.0 ومشاركة الاعتماديات
المشكلة الكلاسيكية كانت تحميل الاعتماديات المشتركة عدة مرات. Module Federation 2.0، خصوصًا عند استخدامه مع أدوات البناء الحديثة، يعالج هذا الأمر بذكاء من خلال مفهوم “Runtime Sharing”. يمكنك أن تخبر كل واجهة مصغرة: “يا جماعة، كلنا سنستخدم نسخة واحدة فقط من React. اللي عنده نسخة متوافقة يستخدمها، واللي ما عنده، هيها موجودة عند التطبيق المضيف (Host)”.
هذا يقلل حجم الحزمة (Bundle Size) بشكل كبير ويحسن أداء التحميل الأولي للصفحة.
نصيحة من أبو عمر: عند استخدام `shared` في إعدادات Module Federation، استخدم خاصية `singleton: true` للمكتبات التي لا يمكن أن تتواجد منها إلا نسخة واحدة في الصفحة مثل `react` و `react-dom`. هذا يجنبك مشاكل غريبة عجيبة قد تظهر بسبب وجود حالتين (states) مختلفتين من React في نفس الوقت. صدقني، هاي “طبخة بحص” ما بدك تجربها.
// في ملف rspack.config.js أو webpack.config.js للتطبيق المضيف (Host)
const { ModuleFederationPlugin } = require('@rspack/core').container;
module.exports = {
// ...
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
// 'products' هو الاسم الذي سنستخدمه لاستيراد الوحدات من تطبيق المنتجات
// 'products@http://localhost:3001/remoteEntry.js' هو عنوان التطبيق البعيد
products: 'products@http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};
// في ملف إعدادات تطبيق المنتجات (Remote)
new ModuleFederationPlugin({
name: 'products',
filename: 'remoteEntry.js',
exposes: {
// './ProductPage' هو المسار الذي نكشفه للعالم الخارجي
'./ProductPage': './src/ProductPage',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
لاحظ كيف اتفق التطبيقان على مشاركة React و React-DOM كنسخة فريدة.
المستقبل في 2026: التنسيق المدعوم بالذكاء الاصطناعي (AI-Driven Orchestration)
حسناً، لقد قسّمنا التطبيق وربطناه بشكل فعال. الآن، كيف ننتقل من الأداء الجيد إلى الأداء الخارق؟ الجواب يكمن في الانتقال من التحميل الكسول (Lazy Loading) الثابت إلى التحميل الاستباقي الذكي (Predictive Prefetching).
وداعًا للقواعد الثابتة، مرحبًا بـ DAIMA
دعنا نقدم مفهومًا أسميته DAIMA (Dynamic AI-Orchestrated Modular Architecture) أو “بنية الوحدات الديناميكية المنسقة بالذكاء الاصطناعي”. الفكرة بسيطة ومذهلة في نفس الوقت: بدلاً من أن أقول “إذا ضغط المستخدم على زر التقارير، قم بتحميل وحدة التقارير”، ماذا لو استطاع النظام التنبؤ بما سيفعله المستخدم تالياً وتحميله مسبقاً؟
هنا كيف يعمل النظام:
- جمع البيانات: يقوم التطبيق بجمع بيانات مجهولة المصدر عن مسارات تنقل المستخدمين. مثلاً: “فتح صفحة التقارير” -> “ضغط زر التصدير إلى Excel” -> “فتح صفحة إعدادات التصدير”.
- التدريب والتعلم: يتم إرسال هذه البيانات إلى نموذج تعلم آلة بسيط (يمكن استخدام Reinforcement Learning أو حتى نماذج احتمالية أبسط مثل سلسلة ماركوف). يتعلم النموذج الأنماط الشائعة. مثلاً، يكتشف أن 85% من المستخدمين الذين يزورون صفحة “التقارير الشهرية” يضغطون على زر “التصدير” خلال الدقيقة التالية.
- التنبؤ والتنفيذ: عندما يزور مستخدم جديد صفحة “التقارير الشهرية”، يقوم “المنسق الذكي” (AI Orchestrator) في التطبيق بسؤال النموذج: “ما هي الخطوة التالية المحتملة؟”. يرد النموذج: “وحدة التصدير، بنسبة ثقة 85%”.
- التحميل المسبق: بناءً على هذا التنبؤ، يقوم التطبيق بتحميل حزمة JavaScript الخاصة بوحدة “التصدير” بهدوء في الخلفية. عندما يضغط المستخدم فعليًا على زر “التصدير”، تكون الوحدة جاهزة وموجودة في ذاكرة التخزين المؤقت للمتصفح، فتظهر بشكل فوري!
بهذه الطريقة، نحصل على أفضل ما في العالمين: مرونة الواجهات المصغرة، وسرعة واستجابة التطبيق المتجانس (Monolith).
Rspack: المحرك النفاث لتطبيقاتك
كل هذا الكلام الجميل عن الواجهات المصغرة والذكاء الاصطناعي يحتاج إلى أداة بناء (Bundler) سريعة وقوية. لسنوات، كان Webpack هو الخيار الأول، لكن مع نمو المشاريع، أصبح بطؤه مؤلمًا. أتذكرون قصة فنجان القهوة؟
هنا يأتي دور Rspack. هو أداة بناء حديثة، متوافقة إلى حد كبير مع Webpack، لكنها مكتوبة بلغة Rust. لماذا هذا مهم؟ لأن Rust تمنح Rspack سرعة خرافية بفضل قدرتها على الاستفادة من المعالجة المتوازية (Parallel Processing) والتحكم الدقيق في الذاكرة.
الانتقال من Webpack إلى Rspack في كثير من الأحيان لا يتطلب سوى تغييرات طفيفة في ملف الإعدادات، لكن فارق السرعة في البناء يمكن أن يصل إلى 10 أضعاف! هذا يعني دورات تطوير أسرع، تجربة أفضل للمطورين، وقدرة على نشر التحديثات بشكل أسرع.
نصيحة عملية من أبو عمر: لا تنتظر حتى يصبح مشروعك عملاقًا لتبدأ بالتفكير في الأداء. ابدأ باستخدام أدوات سريعة مثل Rspack من اليوم الأول. جربه في مشروعك القادم، أو حتى قم بترحيل مشروع صغير قائم. الفرق في سرعة البناء سيجعلك تتساءل كيف كنت تعيش بدونه.
الخلاصة: نحن لا نبني تطبيقات، بل مدنًا رقمية 🏙️
رحلتنا في بناء الواجهات الأمامية الحديثة تشبه إلى حد كبير تخطيط المدن. بدأنا بـ “الأكواخ” المتجانسة (Monoliths)، ثم انتقلنا إلى بناء “أحياء” متخصصة ومستقلة (Micro-Frontends). تعلمنا كيف نبني “جسورًا” ذكية بينها (Module Federation) لضمان التدفق السلس للحركة. والآن، نحن على أعتاب استخدام “أنظمة مرور ذكية” (AI Orchestration) تتنبأ بالازدحام وتفتح طرقًا جديدة قبل أن يطلبها أحد.
الهدف النهائي هو بناء تجربة مستخدم سلسة، سريعة، وممتعة، بغض النظر عن حجم وتعقيد النظام الذي يقف خلفها. كمطورين ومهندسي برمجيات، هذه هي مهمتنا، وهذا هو التحدي الممتع الذي نواجهه كل يوم.
فلا تخافوا من تجربة الجديد وتفكيك القديم. عالم البرمجة، مثل الحياة تمامًا، من يقف فيه مكانه يرجع للخلف. استمروا في التعلم والتجربة، فهكذا نبني المستقبل، كودًا بكود. يلا بالتوفيق يا شباب! 💪