العودة إلى المدونة

كيف تنظم بنية مشروع Go (2026): دليل الهيكلة الاصطلاحية

بنية المجلدات التي ينسخها معظم المبتدئين من مستودع يحمل 50 ألف نجمة ليست معيارًا رسميًا في Go، وفريق Go نفسه يقول ذلك. إليك الطريقة الرسمية الحقيقية لتنظيم مشروع Go، وهي أبسط مما تتوقع.

كيف تنظم بنية مشروع Go (2026): دليل الهيكلة الاصطلاحية

ابحث عن «بنية مشروع Go» وخلال نقرتين ستصل إلى golang-standards/project-layout، وهو مستودع على GitHub يحمل أكثر من 50,000 نجمة وشجرة مجلدات مليئة بـ cmd/ وinternal/ وpkg/ وapi/ وbuild/. يبدو المستودع مرجعًا موثوقًا، ومعظم المبتدئين ينسخونه كما هو في برنامج لا يتجاوز خمسة ملفات.

لكنه ليس معيارًا رسميًا، وقد صرّح فريق Go بذلك علنًا. الإرشادات الرسمية أبسط: ابدأ ببنية مسطحة، ولا تضف مجلدات إلا عندما يحتاجها الكود. تتبع الأقسام التالية هذه الإرشادات مجلدًا تلو الآخر، مع أداة CLI للطقس وAPI لمتجر كتب كمثالين يرافقاننا طوال المقال.

ما هي البنية القياسية لمشروع Go؟

لا توجد شجرة مجلدات إلزامية واحدة. لا تشترط Go وجود src/ أو cmd/، ولا أي مجلد على الإطلاق سوى ملف go.mod في جذر الوحدة. يربط الدليل الرسمي «Organizing a Go module» البنية بحجم المشروع. ويعرض أربع بنى، كل واحدة منها خطوة أبعد من سابقتها: حزمة أساسية، ثم أمر أساسي، ثم حزمة تنمو معها حزم مساندة، ثم مشروع «خادم» (server) يضم عدة ملفات تنفيذية. (المصدر)

لذلك فالسؤال المفيد هو مقدار البنية التي يحتاجها مشروعك الآن، والجواب لمعظم البرامج أنه يحتاج القليل جدًا. يستعرض باقي هذا المقال البنى الأربع من الأبسط إلى الأكثر تعقيدًا. ابحث عن البنية التي تناسب مشروعك وتوقف عندها.

ابدأ ببنية مسطحة: حزمة واحدة هي الأسلوب الاصطلاحي

أول بنيتين في المستند الرسمي هما حزمة واحدة وأمر واحد، وكلاهما موجود مباشرة في جذر الوحدة دون أي مجلدات فرعية. في أداة سطر أوامر صغيرة تقرأ اسم مدينة وتطبع حالة الطقس، هذا هو المشروع كله:

example.texttext
weather/
├── go.mod
├── main.go
├── weather.go
└── weather_test.go

يعرّف go.mod مسار الوحدة (مثلًا module github.com/you/weather). ويحتوي main.go على package main ونقطة الدخول. ويحتوي weather.go على المنطق، في الحزمة نفسها. أما weather_test.go فيحتوي على الاختبارات، بجوار الكود الذي تغطيه، وهذا هو المكان الذي تتوقعه Go. يمكنك نشر هذا المشروع كما هو. يسميه المستند الرسمي «أمرًا أساسيًا» (basic command)، وهو الشكل المناسب لمعظم الأدوات التي ستكتبها في سنتك الأولى.

قاعدة واحدة تفسر معظم قرارات البنية التي تأتي لاحقًا: المجلد هو الحزمة. كل ملف في المجلد يعلن الحزمة نفسها، واسم الحزمة هو آخر عنصر في مسار الاستيراد. أضف مجلد weather/store/ وسيحتوي كل ملف فيه على package store. تستورده باسم github.com/you/weather/store وتستدعيه بالشكل store.Get.

لا تضف مجلدات بعد. مجلد utils/ يحتوي على دالة واحدة، أو مجلد models/ يحتوي على struct واحد، يضيف مسار استيراد وحدودًا لحزمة جديدة ولا يعطيك شيئًا في المقابل. أبقِ المشروع مسطحًا إلى أن يبدأ التسطيح في التسبب بمشكلات حقيقية.

متى تضيف مجلد internal/؟

أول سبب وجيه لإضافة بنية هو الخصوصية. لدى Go مستويان لظهور المعرّفات: الأسماء التي تبدأ بحرف كبير مُصدَّرة، والأسماء التي تبدأ بحرف صغير خاصة بالحزمة. هذا يكفي داخل حزمة واحدة. لكن عندما تضم وحدتك عدة حزم، ستحتاج غالبًا إلى كود تستطيع كل حزمة داخل وحدتك استخدامه ولا يستطيع أي كود خارجها استيراده. هذا ما يوفره internal/، والمصرّف يفرضه.

هذا جزء من اللغة نفسها وليس عُرفًا في التسمية. الحزمة الموجودة تحت مجلد اسمه internal/ لا يمكن استيرادها إلا من كود يقع ضمن المجلد الأب لمجلد internal/ هذا، والاستيراد من وحدة أخرى يفشل في البناء. يوصي المستند الرسمي بإبقاء الحزم داخل internal «قدر الإمكان». الكود الخارجي لا يستطيع استيرادها، فلا يمكن أن يصبح معتمدًا عليها، ولن تجد نفسك مضطرًا لدعم API لم تقصد نشرها. (المصدر)

لنفترض أن أداة CLI الخاصة بالطقس تحولت إلى HTTP API صغيرة لمتجر كتب. أصبح فيها الآن التحقق من هوية الطلبات (authentication) وطبقة للوصول إلى البيانات، ولا مكان لأي منهما في API عامة:

example.texttext
bookstore/
├── go.mod
├── main.go
└── internal/
    ├── auth/
    │   ├── auth.go
    │   └── auth_test.go
    └── store/
        ├── store.go
        └── store_test.go

main.go موجود داخل الوحدة bookstore، لذلك يمكنه استيراد github.com/you/bookstore/internal/auth وgithub.com/you/bookstore/internal/store بحرية. أما الكود في أي وحدة أخرى فلا يستطيع ذلك. إذا نفّذ أحدهم go get على وحدتك وحاول استيراد تلك الحزم، يرفض المصرّف الاستيراد. وبما أن لا أحد خارج وحدتك يستطيع الاعتماد على auth أو store، يمكنك إعادة تسمية دوالهما أو تغيير تواقيعها أو إعادة تنظيمهما متى أردت دون أن تكسر كود أي شخص آخر.

متى تضيف مجلد cmd/؟

cmd/ مخصص للمستودعات التي تبني أكثر من ملف تنفيذي، أو التي توزّع ملفًا تنفيذيًا ومكتبة قابلة للاستيراد معًا. إذا كان لديك ملف تنفيذي واحد فقط فلا تحتاج إليه، ويكون main.go في الجذر أنظف.

داخل cmd/ يحصل كل ملف تنفيذي على مجلد فرعي خاص به، ويصبح اسم المجلد اسم البرنامج. تتبع بنية «مشروع الخادم» (server project) الرسمية هذا الأسلوب: كل أمر يوجد في cmd/<name>/ مع ملف main.go صغير يربط الحزم القادمة من internal/ ببعضها. (المصدر) لنفترض أن متجر الكتب أصبح يوزّع خادم API إضافة إلى أداة لترحيل قاعدة البيانات (migration):

example.texttext
bookstore/
├── go.mod
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── migrate/
│       └── main.go
└── internal/
    ├── auth/
    └── store/

ينتج go build ./cmd/api ملفًا تنفيذيًا اسمه api، وينتج go build ./cmd/migrate الملف migrate. يبقى كلا ملفي main.go خفيفين ويستوردان العمل الفعلي من internal/.

إليك التدرج كله في جدول واحد. اختر الصف الذي يطابق مشروعك كما هو اليوم، لا المشروع الذي تتخيل أنه سيصبح عليه.

شكل المشروعالبنية المقترحةالسبب
أداة أو سكربت صغيرمسطحة: main.go مع ملفات المنطق في الجذرحزمة واحدة أسلوب اصطلاحي ومكتمل
ملف تنفيذي واحد مع مكونات داخلية قابلة لإعادة الاستخدامأضف internal/ للحزم الخاصةخصوصية يفرضها المصرّف، دون API سابقة لأوانها
ملفان تنفيذيان أو أكثرأضف cmd/<name>/ لكل ملف تنفيذيكل main يحصل على مجلده الخاص
مستودع متعدد الخدمات (ملفات تنفيذية مع كود مشترك)cmd/ مع internal/، مقسمة حسب المجالبنية «مشروع الخادم» الرسمية

خرافة golang-standards/project-layout

يحمل مستودع golang-standards/project-layout كلمة «standards» في اسم منظمته على GitHub، ولديه أكثر من 50,000 نجمة، وشجرة كبيرة من api/ وbuild/ وconfigs/ وdeployments/ وpkg/ وtest/. من السهل أن تفترض أن فريق Go قد اعتمده، لكنه لم يفعل. يتضمن ملف README في المستودع الآن تنبيهًا يقول إنه ليس معيارًا رسميًا، والاعتراض جاء من قيادة Go نفسها.

في النقاش رقم 117، كتب Russ Cox، أحد القادة التقنيين لمشروع Go، أن هذه «ليست بنية قياسية لمشاريع Go»، وأن معظم مستودعات Go «أبسط بكثير» ولا تستخدم مجلد pkg/ أصلًا. وصل النقاش إلى الصفحة الأولى في Hacker News، حيث اتفق معه معظم مطوري Go ذوي الخبرة. قد تكون هذه البنية منطقية لمستودع موحّد (monorepo) كبير تتشاركه عدة فرق، لكنها تضر كقالب بداية لمشروع عادي. انسخها إلى برنامج صغير وستجد ملفاتك الخمسة تحت ثمانية مجلدات شبه فارغة.

تنتج عن ذلك بعض القواعد، وهي تتفق مع ما ينصح به عادةً ممارسو Go مثل Eli Bendersky وAlex Edwards:

  • لا تستخدم مجلد src/. هذه العادة قادمة من Java ومشاريع Node القديمة. يوجد كود Go في جذر الوحدة، ولا توجد أي بنية رسمية فيها src/.
  • لا تنشئ مجلدات متداخلة قبل أن يكون لديك كود تضعه فيها. مجلدات api/ وbuild/ وdeployments/ الفارغة لا تفعل شيئًا. أنشئ المجلد يوم يصبح لديك ما تضعه فيه.
  • سمِّ الحزم بما تقدمه، لا بدورها. الحزمة التي تسمى utils أو common أو helpers لا تخبر القارئ بشيء، وتتحول إلى مكان تُرمى فيه الأشياء المتفرقة. سمّها store أو auth أو weather أو retry أو slugify. تنصح Go رسميًا بأن تكون أسماء الحزم قصيرة وواضحة وأن تُقرأ جيدًا في موضع الاستدعاء، لذلك store.Get أفضل من utils.GetFromStore. (المصدر)

للأخطاء التي تظهر في الكود لا في المجلدات، اطلع على دليلنا عن الأخطاء الشائعة في Go وكيف تتجنبها.

قاعدة عامة للمبتدئين في تنظيم بنية مشروع Go

يتلخص هذا التدرج في بضعة أسئلة عن مشروعك. أجب عنها من الأعلى، ولا تضف مجلدًا إلا عندما تستدعي الإجابة ذلك.

flowchart TD
    A["New Go project"] --> B["Is it one package<br/>of related code?"]
    B -->|Yes| C["Keep it flat:<br/>main.go + logic in the root"]
    B -->|"Grew past that"| D["Need to hide code<br/>from outside importers?"]
    D -->|Yes| E["Add internal/<br/>put private packages there"]
    E --> F["More than one<br/>binary to build?"]
    F -->|No| G["Done. main.go in root,<br/>logic in internal/"]
    F -->|Yes| H["Add cmd/name/<br/>one dir per binary"]
    H --> I["Many services sharing code?"]
    I -->|Yes| J["Server layout:<br/>cmd/ + internal/ by domain"]

كل خطوة تلبي حاجة محددة: حدًا بين الحزم، أو ملفًا تنفيذيًا ثانيًا، أو كودًا تريد إبقاءه خاصًا. إذا لم تستطع الإشارة إلى واحدة منها، فأنت على الأرجح لا تحتاج إلى المجلد. ابدأ ببنية مسطحة، وأضف internal/ عندما تريد أن يفرض المصرّف الخصوصية، وأضف cmd/ عندما يصبح لديك ملف تنفيذي ثانٍ، ولا تنتقل إلى بنية الخادم الكاملة إلا عندما يضم المستودع فعلًا عدة خدمات.

الأسئلة الشائعة

هل توجد بنية رسمية لمشاريع Go؟

لا، لا توجد بنية مجلدات إلزامية واحدة. يصف المستند الرسمي «Organizing a Go module» بنية تكبر مع حجم المشروع بدلًا من بنية ثابتة واحدة. تبدأ بحزمة مسطحة ثم internal/ ثم cmd/ ثم بنية خادم كاملة. استخدم أصغر بنية تناسب مشروعك. (المصدر)

هل ينبغي استخدام مجلد pkg/؟

غالبًا لا. أشار Russ Cox، أحد القادة التقنيين لمشروع Go، إلى أن معظم مستودعات Go لا تستخدم مجلد pkg/ و«تميل إلى أن تكون أبسط بكثير». ضع الكود الخاص في internal/، حيث يفرض المصرّف الخصوصية، ولا تعرض الحزم في جذر الوحدة إلا عندما تخطط لدعمها كواجهة API عامة. (المصدر)

ما الذي يوضع في internal/ وما الذي يوضع في cmd/؟

يحتوي internal/ على كود مكتبة قابل للاستيراد لا يمكن استخدامه إلا داخل وحدتك، والمصرّف يفرض ذلك. ويحتوي cmd/<name>/ على حزمة main خفيفة لكل ملف تنفيذي تبنيه، تربط الكود القادم من internal/ ببعضه. المنطق يوضع في internal/، ونقاط الدخول توضع في cmd/. (المصدر)

هل يمكن لمشروع Go حقيقي أن يتكون من ملف واحد أو حزمة واحدة؟

نعم. أول مثال في دليل البنية الرسمي هو حزمة واحدة في جذر الوحدة، وكثير من الأدوات المستخدمة في بيئة الإنتاج تُوزَّع بهذا الشكل. حزمة واحدة مع go.mod وmain.go واختباراتها أسلوب اصطلاحي ومكتمل. أضف بنية عندما يكبر الكود عليها. (المصدر)

ما الفرق بين مشروع Go ومشروع Java أو Node؟

لا يوجد في Go مجلد src/. يوجد الكود في جذر الوحدة، وكل مجلد هو حزمة، واسم الحزمة هو آخر عنصر في المسار. لا تفرض أي أداة بناء شجرة مجلدات كما يفعل Maven أو أداة التجميع (bundler)، لذلك تبقى بنى Go أكثر تسطحًا ولا تكبر إلا عند الحاجة. (المصدر)

الخطوة التالية

البنية من الأجزاء الأسهل في Go. توزّع برنامجًا ببنية مسطحة، ثم تضيف internal/ وcmd/ عندما يحتاجهما الكود. إذا لم تستطع أن تقول لماذا يوجد مجلد ما، فيمكنك على الأرجح حذفه.

أفضل طريقة لاكتساب الإحساس بذلك هي بناء برنامجين حقيقيين، مثل أداة CLI وخدمة HTTP، وترك البنية تنمو أثناء العمل. يغطي مسار Clean Go Code في LevelUpGo تصميم الحزم الاصطلاحي وبنية المشاريع من خلال تمارين تكتبها وتشغلها في المتصفح، ويبدأ معك مسار Go Fundamentals من أول ملف go.mod ومن أدوات Go الأساسية (toolchain). يمكنك أيضًا تصفح خارطة الطريق التفاعلية الكاملة.

اكتب Go كما يكتبها مهندس أول

دروس تفاعلية في متصفحك. الدروس الأولى مجانية.

جرّب درسًا مجانيًاأو أنشئ حسابًا مجانيًا