دليل Monorepos لكود الواجهة الأمامية
نشرت: 2022-03-11Monorepos هي موضوع ساخن للمناقشة. كان هناك الكثير من المقالات مؤخرًا حول سبب وجوب عدم استخدام هذا النوع من الهندسة في مشروعك ، ولكن معظمها متحيز بطريقة أو بأخرى. هذه السلسلة هي محاولة لجمع وشرح أكبر قدر ممكن من المعلومات لفهم كيف ومتى يتم استخدام monorepos.
يعد Monorepository مفهومًا معماريًا يحتوي أساسًا على كل المعاني في عنوانه. بدلاً من إدارة مستودعات متعددة ، يمكنك الاحتفاظ بجميع أجزاء التعليمات البرمجية المعزولة داخل مستودع واحد. ضع في اعتبارك كلمة معزولة - فهذا يعني أن monorepo ليس له أي قاسم مشترك مع تطبيقات monolithic. يمكنك الاحتفاظ بالعديد من أنواع التطبيقات المنطقية داخل ريبو واحد ؛ على سبيل المثال ، موقع ويب وتطبيق iOS الخاص به.
هذا المفهوم قديم نسبيًا وظهر منذ حوالي عقد من الزمان. كانت Google واحدة من أولى الشركات التي تبنت هذا النهج لإدارة قواعد الرموز الخاصة بها. قد تسأل ، إذا كانت موجودة منذ عقد من الزمان ، فلماذا أصبحت موضوعًا ساخنًا الآن فقط؟ في الغالب ، على مدار 5-6 سنوات الماضية ، خضعت أشياء كثيرة لتغييرات جذرية. ES6 ، ومعالجات SCSS الأولية ، ومديرو المهام ، و npm ، وما إلى ذلك - في الوقت الحاضر ، للحفاظ على تطبيق صغير قائم على React ، عليك التعامل مع حزم المشاريع ، ومجموعات الاختبار ، ونصوص CI / CD ، وتكوينات Docker ، ومن يعرف ماذا أيضًا. والآن تخيل أنه بدلاً من تطبيق صغير ، تحتاج إلى صيانة منصة ضخمة تتكون من الكثير من المجالات الوظيفية. إذا كنت تفكر في الهندسة المعمارية ، فستحتاج إلى القيام بأمرين رئيسيين: الاهتمامات المنفصلة وتجنب مغفل الكود.
لتحقيق ذلك ، ربما ترغب في عزل الميزات الكبيرة في بعض الحزم ثم استخدامها عبر نقطة دخول واحدة في تطبيقك الرئيسي. لكن كيف تدير هذه الحزم؟ يجب أن يكون لكل حزمة تكوين بيئة سير العمل الخاص بها ، وهذا يعني أنه في كل مرة تريد إنشاء حزمة جديدة ، سيتعين عليك تكوين بيئة جديدة ، ونسخ جميع ملفات التكوين ، وما إلى ذلك. أو ، على سبيل المثال ، إذا كان عليك تغيير شيء ما في نظام البناء الخاص بك ، فسيتعين عليك مراجعة كل إعادة ، والقيام بالتزام ، وإنشاء طلب سحب ، وانتظار كل بناء ، مما يؤدي إلى إبطائك كثيرًا. في هذه الخطوة ، نجتمع مع monorepos.
بدلاً من وجود الكثير من المستودعات مع التكوينات الخاصة بها ، سيكون لدينا مصدر واحد فقط للحقيقة - monorepo: عداء مجموعة اختبار واحد ، وملف تكوين Docker واحد ، وتكوين واحد لـ Webpack. ولا يزال لديك قابلية التوسع ، والفرصة لفصل الاهتمامات ، ومشاركة التعليمات البرمجية مع الحزم المشتركة ، والكثير من المحترفين الآخرين. يبدو لطيفا ، أليس كذلك؟ حسنا هي كذلك. ولكن هناك بعض العيوب أيضًا. دعونا نلقي نظرة فاحصة على إيجابيات وسلبيات استخدام monorepo في البرية.
مزايا Monorepo:
- مكان واحد لتخزين جميع التكوينات والاختبارات. نظرًا لوجود كل شيء داخل ريبو واحد ، يمكنك تكوين CI / CD والمجمع مرة واحدة ثم إعادة استخدام التكوينات لإنشاء جميع الحزم قبل نشرها على جهاز التحكم عن بعد. ينطبق الأمر نفسه على اختبارات الوحدة ، و e2e ، والتكامل - سيكون CI الخاص بك قادرًا على بدء جميع الاختبارات دون الحاجة إلى التعامل مع التكوين الإضافي.
- أعد تشكيل الخصائص العالمية بسهولة باستخدام الالتزامات الذرية. بدلاً من إجراء طلب سحب لكل ريبو ، وتحديد الترتيب الذي ستبني به تغييراتك ، ما عليك سوى تقديم طلب سحب ذري يحتوي على جميع الالتزامات المتعلقة بالميزة التي تعمل ضدها.
- نشر حزمة مبسطة. إذا كنت تخطط لتنفيذ ميزة جديدة داخل حزمة تعتمد على حزمة أخرى برمز مشترك ، فيمكنك القيام بذلك باستخدام أمر واحد. إنها وظيفة تحتاج إلى بعض التكوينات الإضافية ، والتي ستتم مناقشتها لاحقًا في جزء مراجعة الأدوات من هذه المقالة. حاليًا ، هناك مجموعة غنية من الأدوات ، بما في ذلك Lerna و Yarn Workspaces و Bazel.
- إدارة التبعية أسهل. حزمة واحدة فقط. json . لا حاجة لإعادة تثبيت التبعيات في كل ريبو متى أردت تحديث تبعياتك.
- أعد استخدام الكود مع الحزم المشتركة مع الاحتفاظ بها معزولة. يتيح لك Monorepo إعادة استخدام الحزم الخاصة بك من الحزم الأخرى مع إبقائها معزولة عن بعضها البعض. يمكنك استخدام مرجع للحزمة البعيدة واستهلاكها عبر نقطة دخول واحدة. لاستخدام الإصدار المحلي ، يمكنك استخدام روابط الرموز المحلية. يمكن تنفيذ هذه الميزة عبر البرامج النصية bash أو عن طريق تقديم بعض الأدوات الإضافية مثل Lerna أو Yarn.
عيوب Monorepo:
- لا توجد طريقة لتقييد الوصول إلى بعض أجزاء التطبيق فقط. لسوء الحظ ، لا يمكنك مشاركة جزء monorepo الخاص بك فقط - سيتعين عليك منح حق الوصول إلى قاعدة التعليمات البرمجية بالكامل ، مما قد يؤدي إلى بعض المشكلات الأمنية.
أداء Git ضعيف عند العمل في مشاريع كبيرة الحجم. تبدأ هذه المشكلة في الظهور فقط على التطبيقات الضخمة التي تحتوي على أكثر من مليون التزام ويقوم المئات من المطورين بعملهم في وقت واحد كل يوم على نفس الريبو. يصبح هذا الأمر مزعجًا بشكل خاص حيث يستخدم Git مخططًا لا دوريًا موجهًا (DAG) لتمثيل تاريخ المشروع. مع وجود عدد كبير من الالتزامات ، يمكن أن يصبح أي أمر يمشي في الرسم البياني بطيئًا مع تعمق التاريخ. يتباطأ الأداء أيضًا بسبب عدد المراجع (على سبيل المثال ، الفروع أو العلامات ، القابلة للحل عن طريق إزالة المراجع التي لم تعد بحاجة إليها) وكمية الملفات المتعقبة (بالإضافة إلى وزنها ، على الرغم من إمكانية حل مشكلة الملفات الثقيلة باستخدام بوابة LFS).
ملاحظة: في الوقت الحاضر ، يحاول Facebook حل المشكلات المتعلقة بقابلية توسع VCS عن طريق تصحيح Mercurial ، وربما قريبًا ، لن تكون هذه مشكلة كبيرة.
- وقت بناء أعلى. نظرًا لأنه سيكون لديك الكثير من التعليمات البرمجية المصدر في مكان واحد ، فسوف يستغرق الأمر مزيدًا من الوقت حتى يقوم CI بتشغيل كل شيء من أجل الموافقة على كل العلاقات العامة.
مراجعة الأداة
تتزايد مجموعة أدوات إدارة monorepos باستمرار ، وفي الوقت الحالي ، من السهل حقًا أن تضيع في مجموعة متنوعة من أنظمة البناء الخاصة بـ monorepos. يمكنك دائمًا أن تكون على دراية بالحلول الشائعة باستخدام هذا الريبو. لكن في الوقت الحالي ، دعنا نلقي نظرة سريعة على الأدوات المستخدمة بكثرة في الوقت الحاضر مع JavaScript:
- Bazel هو نظام بناء Google monorepo الموجه. المزيد على Bazel: awesome-bazel
- Yarn هي أداة لإدارة تبعية JavaScript تدعم monorepos من خلال مساحات العمل.
- Lerna هي أداة لإدارة مشاريع JavaScript بحزم متعددة مبنية على Yarn.
تستخدم معظم الأدوات نهجًا مشابهًا حقًا ، ولكن هناك بعض الفروق الدقيقة.

سوف نتعمق أكثر في سير عمل Lerna وكذلك في الأدوات الأخرى في الجزء 2 من هذه المقالة لأنه موضوع كبير نوعًا ما. في الوقت الحالي ، دعنا نلقي نظرة عامة على ما بالداخل:
ليرنا
تساعد هذه الأداة حقًا أثناء التعامل مع الإصدارات الدلالية ، وإعداد سير عمل البناء ، ودفع الحزم الخاصة بك ، وما إلى ذلك. الفكرة الرئيسية وراء Lerna هي أن مشروعك يحتوي على مجلد حزم ، والذي يحتوي على جميع أجزاء الكود المعزولة. وإلى جانب الحزم ، لديك تطبيق رئيسي ، والذي على سبيل المثال يمكنه العيش في مجلد src. تعمل جميع العمليات في Lerna تقريبًا من خلال قاعدة بسيطة - تقوم بالتكرار خلال جميع الحزم الخاصة بك ، وتقوم ببعض الإجراءات عليها ، على سبيل المثال ، زيادة إصدار الحزمة ، وتحديث تبعية جميع الحزم ، وبناء جميع الحزم ، وما إلى ذلك.
مع Lerna ، لديك خياران حول كيفية استخدام حزمك:
- بدون دفعهم إلى جهاز التحكم عن بعد (NPM)
- دفع الحزم الخاصة بك إلى جهاز التحكم عن بعد
أثناء استخدام الطريقة الأولى ، يمكنك استخدام المراجع المحلية لحزمك ولا تهتم حقًا بالارتباطات الرمزية لحلها.
ولكن إذا كنت تستخدم الطريقة الثانية ، فأنت مجبر على استيراد الحزم الخاصة بك من جهاز التحكم عن بعد. (على سبيل المثال ، import { something } from @yourcompanyname/packagename;
) ، مما يعني أنك ستحصل دائمًا على الإصدار البعيد من حزمتك. بالنسبة للتطوير المحلي ، سيتعين عليك إنشاء ارتباطات رمزية في جذر المجلد الخاص بك لجعل المجمع يحل الحزم المحلية بدلاً من استخدام تلك الموجودة داخل node_modules/
. لهذا السبب ، قبل إطلاق Webpack أو الحزمة المفضلة لديك ، سيتعين عليك lerna bootstrap
، والذي سيربط تلقائيًا جميع الحزم.
غزل
الغزل في البداية هو مدير تبعية لحزم NPM ، والتي لم يتم إنشاؤها في البداية لدعم monorepos. ولكن في الإصدار 1.0 ، أصدر مطورو Yarn ميزة تسمى مساحات العمل . في وقت الإصدار ، لم يكن ذلك مستقرًا ، ولكن بعد فترة ، أصبح قابلاً للاستخدام في مشاريع الإنتاج.
مساحة العمل هي في الأساس حزمة ، لها حزمة خاصة بها ، ويمكن أن تحتوي على بعض قواعد البناء المحددة (على سبيل المثال ، tsconfig.json منفصل إذا كنت تستخدم TypeScript في مشاريعك.). يمكنك في الواقع بطريقة ما الإدارة بدون مساحات عمل Yarn باستخدام bash ولديك نفس الإعداد بالضبط ، ولكن هذه الأداة تساعد في تسهيل عملية التثبيت وتحديث التبعيات لكل حزمة.
في لمحة ، توفر Yarn مع مساحات عملها الميزات المفيدة التالية:
- مجلد واحد
node_modules
في الجذر لجميع الحزم. على سبيل المثال ، إذا كان لديكpackages/package_a
packages/package_b
- معpackage.json
الخاصة بهم - فسيتم تثبيت جميع التبعيات في الجذر فقط. هذا هو أحد الاختلافات بين طريقة عمل Yarn و Lerna. - الارتباط التبعي للسماح بتطوير الحزمة المحلية.
- ملف قفل واحد لجميع التبعيات.
- تحديث التبعيات المركزة في حالة ما إذا كنت تريد إعادة تثبيت التبعيات لحزمة واحدة فقط. يمكن القيام بذلك باستخدام علامة
-focus
. - التكامل مع ليرنا. يمكنك بسهولة جعل Yarn يتعامل مع جميع عمليات التثبيت / الربط الرمزي والسماح لـ Lerna بالاهتمام بالنشر والتحكم في الإصدار. هذا هو الإعداد الأكثر شيوعًا حتى الآن لأنه يتطلب جهدًا أقل ويسهل التعامل معه.
روابط مفيدة:
- مساحات عمل الغزل
- كيفية بناء مشروع TypeScript mono-repo
بازل
Bazel هي أداة بناء للتطبيقات واسعة النطاق ، والتي يمكنها التعامل مع التبعيات متعددة اللغات ودعم الكثير من اللغات الحديثة (Java ، JS ، Go ، C ++ ، إلخ). في معظم الحالات ، يعد استخدام Bazel لتطبيقات JS الصغيرة إلى المتوسطة أمرًا مبالغًا فيه ، ولكن على نطاق واسع ، قد يوفر الكثير من الفوائد بسبب أدائه.
بحكم طبيعتها ، تشبه Bazel أدوات Make و Gradle و Maven وغيرها من الأدوات التي تسمح ببناء المشروع بناءً على الملف الذي يحتوي على وصف لقواعد الإنشاء وتبعيات المشروع. نفس الملف في Bazel يسمى BUILD ويقع داخل مساحة العمل الخاصة بمشروع Bazel. يستخدم ملف BUILD لغة Starlark الخاصة به ، وهي لغة بناء عالية المستوى يمكن قراءتها من قبل الإنسان والتي تشبه إلى حد كبير لغة Python.
عادةً ، لن تتعامل كثيرًا مع BUILD نظرًا لوجود الكثير من النماذج المعيارية التي يمكن العثور عليها بسهولة على الويب والتي تم تكوينها بالفعل وجاهزة للتطوير. عندما تريد بناء مشروعك ، تقوم Bazel بشكل أساسي بما يلي:
- يقوم بتحميل ملفات BUILD ذات الصلة بالهدف.
- يحلل المدخلات وتبعياتها ، ويطبق قواعد البناء المحددة ، وينتج رسم بياني للإجراء.
- ينفذ إجراءات البناء على المدخلات حتى يتم إنتاج مخرجات البناء النهائية.
روابط مفيدة:
- JavaScript and Bazel - Docs لإعداد مشروع Bazel لـ JS من البداية.
- قواعد JavaScript و TypeScript لـ Bazel - Boilerplate لـ JS.
خاتمة
Monorepos هي مجرد أداة. هناك الكثير من الحجج حول ما إذا كان لها مستقبل أم لا ، ولكن الحقيقة هي أنه في بعض الحالات ، تؤدي هذه الأداة وظيفتها وتتعامل معها بطريقة فعالة. على مدار السنوات القليلة الماضية ، تطورت هذه الأداة واكتسبت قدرًا أكبر من المرونة وتغلبت على الكثير من المشكلات وأزالت طبقة التعقيد من حيث التكوين.
لا يزال هناك الكثير من المشكلات التي يجب اكتشافها ، مثل أداء Git الضعيف ، ولكن نأمل أن يتم حل ذلك في المستقبل القريب.
إذا كنت ترغب في تعلم كيفية إنشاء خط أنابيب CI / CD قوي لتطبيقك ، فإنني أوصي بكيفية إنشاء خط أنابيب نشر أولي فعال باستخدام GitLab CI .