مستودع أحادي مع Kotlin: تنظيم الكود وأفضل الممارسات
مع نمو المشاريع وزيادة عدد الخدمات، يواجه المطورون سؤالاً: كيف ننظم الكود بحيث يظل قابلاً للإدارة، وسهل الاختبار، وسريع النشر؟ أحد الأساليب الشائعة هو المستودع الأحادي (monorepo). في هذه المقالة، سنشرح كيفية تنظيم مستودع أحادي بكفاءة باستخدام Kotlin و Gradle، وما هي الأدوات والأنماط التي يجب استخدامها، وكيفية تجنب الأخطاء النموذجية.
ما هو المستودع الأحادي ولماذا هو مطلوب؟
المستودع الأحادي هو نهج يتم فيه تخزين كود عدة مشاريع (وحدات، مكتبات، خدمات) في مستودع واحد. في سياق Kotlin، هذا يعني أنه يمكنك الحصول على مشروع Gradle مشترك، يحتوي بداخله على وحدات لخدمات backend، وتطبيقات Android، والمكتبات المشتركة، وحتى التوثيق.
مزايا المستودع الأحادي:
- إصدار موحد — جميع الوحدات تستخدم إصداراً واحداً من Kotlin والتبعيات المشتركة.
- إعادة هيكلة مبسطة — التغييرات في المكتبة المشتركة تظهر فوراً لجميع المستهلكين.
- إعداد CI/CD مشترك — خط أنابيب واحد لجميع الوحدات.
- تعاون محسّن — يرى المطورون كود المشروع بأكمله.
ومع ذلك، يتطلب المستودع الأحادي انضباطاً: بدون تنظيم صحيح، يمكن أن يتحول إلى "كتلة واحدة" ذات تبعيات متشابكة. يوفر Kotlin و Gradle أدوات ممتازة لمكافحة ذلك.
هيكل المستودع الأحادي في Kotlin
لننظر إلى هيكل مشروع نموذجي، حيث توجد خدمة backend على Ktor، ومكتبة مشتركة لنماذج البيانات، وتطبيق Android:
my-monorepo/├── build.gradle.kts # ملف البناء الجذري├── settings.gradle.kts # ربط الوحدات├── gradle.properties # الإصدارات المشتركة├── common/│ ├── build.gradle.kts│ └── src/│ └── main/kotlin/│ └── com/example/common/│ └── Models.kt├── backend/│ ├── build.gradle.kts│ └── src/│ └── main/kotlin/│ └── com/example/backend/│ └── Application.kt├── android-app/│ ├── build.gradle.kts│ └── src/│ └── main/kotlin/│ └── com/example/android/│ └── MainActivity.kt└── ci/ └── build.shيقوم ملف settings.gradle.kts بربط الوحدات:
rootProject.name = "my-monorepo"
include(":common")include(":backend")include(":android-app")عادةً ما يحتوي ملف build.gradle.kts الجذري على الإضافات والإعدادات المشتركة فقط:
plugins { kotlin("jvm") version "1.9.22" apply false kotlin("android") version "1.9.22" apply false id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" apply false}
allprojects { group = "com.example" version = "1.0.0"}هام: يتم تحديد الإضافات مع apply false، بحيث لا يتم تطبيقها على المشروع الجذري، بل تكون متاحة للوحدات الفرعية.
إدارة التبعيات في المستودع الأحادي
إحدى المهام الرئيسية هي تجنب تكرار إصدارات التبعيات. لهذا، يتم استخدام Gradle Version Catalog (ملف libs.versions.toml في مجلد gradle). مثال:
# gradle/libs.versions.toml[versions]kotlin = "1.9.22"ktor = "2.3.7"coroutines = "1.7.3"
[libraries]ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
[plugins]kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.