إزاي تقسم تطبيقك العملاق؟ بناء معمارية المايكرو فرونت إند (Micro-Frontends) باستخدام Next.js و Module Federation
لو شغال في شركة كبيرة أو مشروع ضخم (Monolith)، أكيد مريت بلحظة "الكابوس" دي: إنك تعدل زرار صغير في صفحة الـ Checkout، فجأة تلاقي صفحة الـ Profile ضربت، أو إن وقت الـ Build بقى بياخد ربع ساعة عشان بس تجرب سطر كود واحد! الحاجة دي بتخلينا ندور على حل يخلينا نقسم المشروع لأجزاء مستقلة، وده اللي بنسميه المايكرو فرونت إند (Micro-Frontends).
Table of contents [Show]
يعني إيه مايكرو فرونت إند (Micro-Frontends) وليه بنحتاجها؟
ببساطة، هي فكرة إنك بتقسم تطبيق الفرونت إند بتاعك لمشاريع صغيرة ومستقلة (Independent Projects). كل فريق بيمسك جزء معين من الموقع، يقدر يطوره، يعمل له Build، وDeploy لوحده خالص من غير ما يأثر على باقي أجزاء التطبيق. ده بيقلل الـ Coupling وبيسرع عملية التطوير بشكل خيالي.
سحر الـ Module Federation
زمان كنا بنستخدم الـ iFrames أو الـ NPM Packages عشان نربط أجزاء الموقع ببعض، وكل طريقة كان ليها مشاكلها. لكن الـ Module Federation اللي وفرته Webpack 5 غيرت اللعبة تماماً. هي تقنية بتخليك تشارك الكود (Share Code) بين المشاريع في وقت التشغيل (Runtime) من غير ما تضطر تعمل Compile للمشروع كله.
إزاي نربط Next.js مع Module Federation؟
بما إننا بنستخدم Next.js، الموضوع بيبقى محتاج شوية ضبط، لأن Next.js عنده طريقة خاصة في التعامل مع الـ Webpack. هنحتاج مكتبة اسمها @module-federation/nextjs-mf عشان تسهل علينا المهمة.
الخطوة الأولى: إعداد الـ next.config.js في التطبيق المستضيف (Host):
const { NextFederationPlugin } = require('@module-federation/nextjs-mf');
module.exports = {
webpack(config, options) {
config.plugins.push(
new NextFederationPlugin({
name: 'host',
filename: 'static/chunks/remoteEntry.js',
remotes: {
app2: 'app2@http://localhost:3001/_next/static/chunks/remoteEntry.js',
},
})
);
return config;
},
};
هنا إحنا بنعرف التطبيق بتاعنا إنه "Host" وإنه بيستقبل كود من "Remote" موجود على بورت 3001.
إزاي نشارك المكونات (Shared Components)
الجمال في الموضوع إنك تقدر تعمل Import لمكون (Component) من مشروع تاني وكأنه موجود عندك محلياً. شوف المثال ده:
import dynamic from 'next/dynamic';
const RemoteButton = dynamic(() => import('app2/Button'), {
ssr: false,
});
export default function HomePage() {
return (
<div>
<h1>أهلاً بيك في التطبيق الرئيسي</h1>
<RemoteButton />
</div>
);
}
تحديات لازم تاخد بالك منها
مش كل حاجة وردي! فيه شوية تحديات لازم تحطها في اعتبارك:
- إدارة الـ Versioning: لو الـ Shared Library اتحدثت، لازم تضمن إن كل التطبيقات متوافقة معاها.
- الـ CSS Isolation: ممكن الستايلات تدخل في بعضها، استخدم CSS Modules أو Tailwind عشان تتفادى المشكلة دي.
- التعقيد (Complexity): المايكرو فرونت إند مش مناسب للمشاريع الصغيرة، استخدمه بس لما يكون فريقك كبير والتطبيق بقى صعب السيطرة عليه.
نصيحة من أخ لمبرمج
بص يا بطل، المعمارية دي قوية جداً بس "مش دايماً هي الحل". ما تروحش تعقد مشروعك بـ Micro-Frontends لمجرد إنها تقنية جديدة وتريند. ابدأ دايماً بالبساطة (KISS Principle). لو حسيت إنك فعلاً محتاج تفصل الفرق عن بعضها، أو إن الـ Build Time بقى بيعطلك، وقتها ابدأ ادرس الموضوع ده. وأهم حاجة، اتعلم الـ Webpack كويس لأنها العمود الفقري لكل ده.
بالتوفيق في مشروعك الجاي، ولو احتجت أي استفسار، أنا موجود!