تعرف على: كودك معزول عن العالم الخارجي
\n\nهل سبق لك أن أعدت كتابة منطق الأعمال فقط لأنك غيرت قاعدة البيانات من PostgreSQL إلى MongoDB؟ أو استيقظت في عرق بارد من فكرة أنك بحاجة لإضافة نقطة نهاية HTTP جديدة، بينما التطبيق بأكمله هو ملف main.go عملاق واحد بطول 3000 سطر؟
إذا كانت الإجابة بنعم - مرحباً بك في النادي. إذا كانت لا - فإما أنك تكتب بلغة Go منذ فترة قصيرة، أو أنك تستخدم بالفعل الهندسة المعمارية السداسية (المعروفة أيضاً باسم Ports & Adapters).
\n\nفي هذه المقالة، سنشرح كيفية بناء تطبيق بلغة Go لا يخاف من التغيير. لا تغيير قاعدة البيانات، ولا الانتقال من REST إلى gRPC، ولا إطار عمل جديد سيجبرك على إعادة كتابة نواة النظام. فقط كود. فقط Go. بدون حشو.
\n\nما هي الهندسة المعمارية السداسية بكلمات بسيطة
\n\nتخيل أن تطبيقك هو سفينة فضاء. في المركز - غرفة القيادة (منطق الأعمال). حولها - منافذ الالتحام. من خلال المنافذ تقوم بتوصيل وحدات مختلفة: المحركات (قاعدة البيانات)، أجهزة الاستشعار (طلبات HTTP)، الاتصال بالأرض (قوائم انتظار الرسائل).
\n\nإذا قررت استبدال المحركات - فقط تفصل القديمة وتلصق الجديدة. غرفة القيادة لن تلاحظ حتى الاستبدال. هذا هو الهندسة المعمارية السداسية.
\n\nالمفاهيم الأساسية:
\n- \n
- Port (المنفذ) - واجهة تحدد كيف يمكن للعالم الخارجي التفاعل مع النواة. على سبيل المثال،
UserRepositoryمع دوالSaveوFindByID. \n - Adapter (المحول) - تنفيذ محدد للمنفذ. يمكن أن يكون لمنفذ واحد محولات متعددة: PostgreSQLAdapter، InMemoryAdapter، MockAdapter للاختبارات. \n
- Core (النواة) - منطق الأعمال الذي لا يعرف شيئاً عن العالم الخارجي. يعمل فقط من خلال المنافذ. \n
هذا النهج يعطي ثلاث قدرات خارقة: قابلية الاختبار (للنواة بدون قاعدة بيانات)، المرونة (استبدال أي جزء)، وحدود مسؤولية واضحة.
\n\nلماذا Go مثالية للهندسة المعمارية السداسية
\n\nGo لا تفرض البرمجة كائنية التوجه، لكنها توفر الواجهات - الشيء الوحيد المطلوب لـ Ports & Adapters. الواجهات في Go ضمنية: الهيكل يلبي الواجهة تلقائياً إذا قام بتنفيذ دوالها. هذا يعني أنه يمكنك استبدال المحول دون تغيير سطر واحد في النواة.
\n\nبالإضافة إلى ذلك، Go تُترجم إلى ملف ثنائي، مما يجعل الهندسة المعمارية مفيدة بشكل خاص للخدمات المصغرة. والاختبار المدمج واختبارات الأداء تسمح بالتحقق الفوري من عزل المكونات.
\n\nهيكل المشروع: كيفية تنظيم المجلدات
\n\nلا يوجد معيار واحد، لكن إليك مخططاً مثبتاً يستخدم في مشاريع الإنتاج:
\n\nmyapp/\n├── cmd/\n│ └── app/\n│ └── main.go # نقطة الدخول، تجميع التبعيات\n├── internal/\n│ ├── core/\n│ │ ├── domain/ # الكيانات، كائنات القيمة\n│ │ ├── ports/ # الواجهات (المنافذ)\n│ │ └── services/ # منطق الأعمال (حالات الاستخدام)\n│ └── adapters/\n│ ├── api/ # محولات HTTP/gRPC\n│ ├── db/ # تطبيقات المستودعات\n│ └── mq/ # قوائم انتظار الرسائل\n└── go.mod\n\n\nهام: مجلد internal يمنع الاستيراد من الخارج - مثالي لحماية النواة. المحولات موجودة بشكل منفصل ويمكن استبدالها بسهولة.
نكتب الكود: من الواجهات إلى التنفيذ
\n\nلنقم بإنشاء خدمة بسيطة للعمل مع المستخدمين. نبدأ من النواة.
\n\nالخطوة 1. المجال والمنافذ
\n\n// internal/core/domain/user.go\npackage domain\n\nimport "time"\n\ntype User struct {\n ID string\n Email string\n Name string\n CreatedAt time.Time\n}\n\n// internal/core/ports/repositories.go\npackage ports\n\nimport "myapp/internal/core/domain"\n\ntype UserRepository interface {\n Save(user domain.User) error\n FindByID(id string) (domain.User, error)\n FindByEmail(email string) (domain.User, error)\