مستودع واحد مع Go: تنظيم الكود عمليًا
\n\nالمستودع الواحد (monorepo) هو نهج لإدارة الكود المصدري، حيث يتم تخزين جميع المشاريع والمكتبات والخدمات في مستودع واحد. في عالم Go، أصبح هذا النهج شائعًا بشكل خاص بفضل الدعم المدمج للوحدات ونظام الحزم المرن. في هذه المقالة، سنشرح كيفية تنظيم مستودع واحد في Go عمليًا، وسنستعرض الهيكل النموذجي، وأمثلة الكود، وأفضل الممارسات.
\n\nلماذا نحتاج إلى مستودع واحد في Go؟
\n\nيحل المستودع الواحد عدة مهام رئيسية:
\n- \n
- تبسيط إدارة التبعيات — جميع الحزم الداخلية موجودة في مكان واحد، مما يسهل تحديثها وإعادة هيكلتها. \n
- سجل تغييرات موحد — التزامات ذرية تؤثر على عدة خدمات في وقت واحد تعمل دون مزامنة إضافية. \n
- تسريع CI/CD — يمكن إعداد خطوط أنابيب تقوم ببناء الأجزاء المتغيرة فقط من المشروع. \n
- تحسين جودة الكود — من الأسهل تطبيق معايير موحدة، وأدوات التحليل، ومراجعة الكود. \n
هيكل المستودع الواحد Go: مخطط أساسي
\n\nلنستعرض هيكلًا نموذجيًا للمستودع الواحد في Go. على سبيل المثال، لنأخذ مشروعًا يتكون من خدمتين صغيرتين (api و worker) ومكتبة مشتركة lib:
my-monorepo/\n├── go.work\n├── go.work.sum\n├── apps/\n│ ├── api/\n│ │ ├── go.mod\n│ │ ├── main.go\n│ │ └── handler.go\n│ └── worker/\n│ ├── go.mod\n│ ├── main.go\n│ └── process.go\n├── lib/\n│ ├── go.mod\n│ ├── auth/\n│ │ └── auth.go\n│ └── db/\n│ └── db.go\n├── scripts/\n│ ├── build.sh\n│ └── test.sh\n└── Makefile\n\nالعناصر الرئيسية:
\n- \n
go.work— ملف مساحة العمل Go (Workspace)، الذي يربط جميع الوحدات. \n apps/— دليل التطبيقات (الخدمات الصغيرة، أدوات CLI، إلخ). \n lib/— المكتبات المشتركة التي تستخدمها جميع التطبيقات. \n scripts/— البرامج النصية المساعدة للبناء والاختبار والنشر. \n
إعداد Go Workspace (go.work)
\n\nيوفر Go 1.18+ آلية workspace، التي تسمح بالعمل مع عدة وحدات في وقت واحد. لننشئ ملف go.work في الجذر:
go 1.22\n\nuse (\n ./apps/api\n ./apps/worker\n ./lib\n)\n\nالآن، أثناء التطوير، يمكن تشغيل الأوامر من جذر المشروع، وسيرى Go جميع الوحدات ككيان واحد. على سبيل المثال، لتشغيل الاختبارات لجميع الوحدات:
\n\ngo test ./...\n\nولبناء خدمة معينة:
\n\ncd apps/api && go build -o bin/api .\n\nمثال كود: مكتبة مشتركة وخدمة صغيرة
\n\nلننشئ مكتبة بسيطة للمصادقة في lib/auth/auth.go:
package auth\n\nimport "errors"\n\nvar (\n ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken تتحقق من الرمز وتعيد معرف المستخدم.\nfunc ValidateToken(token string) (string, error) {\n if token == "" {\n return "", ErrInvalidToken\n }\n // هنا يمكن أن يكون التحقق الفعلي من JWT أو استدعاء خدمة خارجية\n return "user-123", nil\n}\n\nالآن نستخدم هذه المكتبة في الخدمة الصغيرة apps/api/handler.go:
package main\n\nimport (\n "encoding/json"\n "net/http"\n "my-monorepo/lib/auth"\n)\n\ntype UserResponse struct {\n UserID string `json:"user_id"`\n}\n\nfunc UserHandler(w http.ResponseWriter, r *http.Request) {\n token := r.Header.Get("Authorization")\n userID, err := auth.ValidateToken(token)\n if err != nil {\n http.Error(w, "Unauthorized", http.StatusUnauthorized)\n return\n }\n\n resp := UserResponse{UserID: user