عندما يبدأ مشروع PHP الأحادي في التصدع
\nهل سبق لك أن وجدت نفسك في موقف تحول فيه تغيير قاعدة البيانات من MySQL إلى PostgreSQL إلى مهمة تستغرق أسبوعًا؟ أو عندما كنت بحاجة إلى إضافة عميل API جديد، وكان الكود متشابكًا جدًا مع الإطار لدرجة أنه كان من الأسهل كتابة كل شيء من الصفر؟ هل يبدو ذلك مألوفًا؟
\nمعظم مطوري PHP يواجهون عاجلاً أم آجلاً "الشعيرية" في المتحكمات والنماذج. توفر أطر العمل مثل Laravel أو Symfony أدوات مريحة، لكنها أيضًا تفرض ارتباطًا صارمًا بها. هناك حل — هندسة Hexagonal (المعروفة أيضًا باسم "المنافذ والمحولات"، أو الهندسة السداسية).
\nفي هذه المقالة، سنشرح كيفية بناء تطبيق PHP لا يعتمد على إطار العمل، أو قاعدة البيانات، أو الخدمات الخارجية. سترى أمثلة كود يمكن تطبيقها فورًا في مشروعك. هيا بنا!
\n\nما هي هندسة Hexagonal ولماذا تحتاجها؟
\nتخيل شكلًا سداسيًا. في المنتصف — منطق عملك (المجال). حوله — "المنافذ" (الواجهات) التي يتواصل من خلالها العالم الخارجي مع النواة. ومن الخارج — "المحولات" التي تنفذ هذه المنافذ: المتحكمات، المستودعات، عملاء API.
\nالفكرة الرئيسية: نواة التطبيق لا تعرف شيئًا عن العالم الخارجي. لا تستورد Laravel، ولا تعرف عن PDO، ولا تستدعي curl. كل التبعية موجهة إلى الداخل — المحولات تعتمد على المنافذ، وليس العكس.
\nمزايا هذا النهج واضحة:
\n- \n
- قابلية الاختبار: يمكن اختبار منطق العمل بدون قاعدة بيانات وطلبات HTTP. \n
- المرونة: استبدال قاعدة البيانات، أو قائمة الانتظار، أو إطار العمل هو مجرد كتابة محول جديد. \n
- نظافة الكود: لا تخلط بين "ماذا تفعل" و"كيف تفعل". \n
يبدو وكأنه سحر؟ دعنا نلقي نظرة على الكود.
\n\nهيكل المشروع: الهيكل العظمي الأساسي
\nعلى سبيل المثال، لنأخذ سيناريو بسيطًا: تسجيل مستخدم. نحتاج إلى حفظ المستخدم في قاعدة البيانات وإرسال بريد ترحيبي.
\nإليك كيف سيبدو هيكل الدليل النموذجي:
\nsrc/\n├── Domain/\n│ ├── Entity/\n│ │ └── User.php\n│ ├── Repository/\n│ │ └── UserRepositoryInterface.php\n│ ├── Service/\n│ │ └── UserRegistrationService.php\n│ └── Event/\n│ └── UserRegisteredEvent.php\n├── Infrastructure/\n│ ├── Persistence/\n│ │ └── DoctrineUserRepository.php\n│ ├── Mail/\n│ │ └── SymfonyMailerAdapter.php\n│ └── Controller/\n│ └── RegisterUserController.php\n└── Application/\n └── UseCase/\n └── RegisterUserUseCase.php\nلاحظ: Domain هو منطق العمل النقي. Infrastructure هي المحولات. Application هي حالات الاستخدام.
الخطوة 1: النواة — المجال والمنافذ
\nالكيان (Entity)
\nلنبدأ بأبسط شيء — المستخدم. إنه مجرد فئة تحتوي على بيانات، بدون تبعيات.
\n<?php\nnamespace App\\Domain\\Entity;\n\nclass User\n{\n public function __construct(\n private readonly string $id,\n private string $name,\n private string $email\n ) {}\n\n public function getId(): string\n {\n return $this->id;\n }\n\n public function getName(): string\n {\n return $this->name;\n }\n\n public function getEmail(): string\n {\n return $this->email;\n }\n}\n\nالمنفذ — واجهة المستودع
\nالآن نحدد كيف سنحفظ المستخدم. هذا هو المنفذ — واجهة سيتم تنفيذها بواسطة المحول.
\n<?php\nnamespace App\\Domain\\Repository;\n\nuse App\\Domain\\Entity\\User;\n\ninterface UserRepositoryInterface\n{\n public function save(User $user): void;\n public function findByEmail(string $email): ?User;\n}\n\nالمنفذ — واجهة خدمة البريد
\nوبالمثل لإرسال الرسائل:
\n<?php\nnamespace App\\Domain\\Service;\n\nuse App\\