مقدمة
مع نمو المشروع في Rust، يطرح السؤال: كيف ننظم الكود بحيث يظل قابلاً للصيانة والتوسع وسهل الاستخدام للفريق؟ أحد الأساليب الشائعة هو المستودع الأحادي (monorepo). في هذه المقالة، سنشرح ما هو المستودع الأحادي، وكيفية تنظيمه باستخدام Cargo workspace، وسنقدم أمثلة عملية. ستتعلم كيفية هيكلة الكود، وإدارة التبعيات، وتجنب الأخطاء النموذجية.
ما هو المستودع الأحادي ولماذا هو ضروري في Rust؟
المستودع الأحادي (أو monorepo) هو مستودع واحد يحتوي على عدة مشاريع (creates) أو حزم. على عكس المستودعات المتعددة، حيث يعيش كل مشروع بشكل منفصل، يجمع المستودع الأحادي كل شيء: المكتبات، الملفات الثنائية، الاختبارات، التكوينات، والوثائق.
مزايا المستودع الأحادي لـ Rust:
- إصدار موحد للتبعيات — جميع الـ creates تستخدم نفس الإصدارات من الحزم الخارجية، مما يقلل من التعارضات.
- تبسيط إعادة الهيكلة — التغييرات في مكتبة واحدة تظهر فوراً في جميع الـ creates التابعة.
- تكامل شامل — يمكن تشغيل الاختبارات والتحليل اللغوي (linting) للمشروع بأكمله بسهولة.
- توثيق مركزي — جميع المستندات و CI/CD موجودة في مكان واحد.
ومع ذلك، فإن المستودع الأحادي لا يناسب الجميع. بالنسبة للمشاريع الصغيرة أو الفرق ذات دورات التطوير المختلفة، من الأفضل اختيار المستودعات المتعددة. ولكن بالنسبة لنظام Rust البيئي، حيث تعتبر النمطية وإعادة استخدام الكود أمراً طبيعياً، يصبح المستودع الأحادي أداة قوية.
تنظيم المستودع الأحادي مع Cargo Workspace
يدعم Cargo آلية workspace — وهي مجموعة من الـ creates التي تُبنى معاً وتستخدم Cargo.lock مشتركاً. دعنا نلقي نظرة على الهيكل الأساسي.
إنشاء workspace
أنشئ دليلاً جذرياً وملف Cargo.toml بدون قسم [package]، ولكن مع قسم [workspace]:
# Cargo.toml الجذري[workspace]members = [ "core", "cli", "web",]الآن، أنشئ الأدلة الفرعية core و cli و web. في كل منها يجب أن يكون ملف Cargo.toml خاص به بهيكل حزمة نموذجي. على سبيل المثال، لـ core:
# core/Cargo.toml[package]name = "my_core"version = "0.1.0"edition = "2021"
[dependencies]serde = { version = "1.0", features = ["derive"] }بالنسبة لـ cli و web، يمكنك تحديد تبعية لـ my_core:
# cli/Cargo.toml[package]name = "my_cli"version = "0.1.0"edition = "2021"
[dependencies]my_core = { path = "../core" }clap = { version = "4.0", features = ["derive"] }الآن، عند تشغيل cargo build من الجذر، سيتم بناء جميع الـ creates، وسيكون Cargo.lock مشتركاً.
هيكل المجلدات والوحدات
الهيكل الموصى به للمستودع الأحادي لـ Rust:
my_monorepo/├── Cargo.toml # workspace├── Cargo.lock├── core/ # مكتبة بأنواع مشتركة│ ├── Cargo.toml│ └── src/│ └── lib.rs├── cli/ # تطبيق وحدة التحكم│ ├── Cargo.toml│ └── src/│ └── main.rs├── web/ # خادم ويب (مثلاً على Actix)│ ├── Cargo.toml│ └── src/│ └── main.rs├── tests/ # اختبارات التكامل (اختياري)│ └── integration_test.rs├── scripts/ # نصوص لـ CI/CD└── README.mdداخل كل create، استخدم هيكل الوحدات القياسي لـ Rust. على سبيل المثال، في core/src/lib.rs:
// core/src/lib.rspub mod models;pub mod utils;وفي core/src/models.rs:
// core/src/models.rsuse serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]pub struct