كيفية ترتيب مشروع PHP: هندسة Hexagonal بدون ألم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

عندما يبدأ مشروع 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

على سبيل المثال، لنأخذ سيناريو بسيطًا: تسجيل مستخدم. نحتاج إلى حفظ المستخدم في قاعدة البيانات وإرسال بريد ترحيبي.

\n

إليك كيف سيبدو هيكل الدليل النموذجي:

\n
src/\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 هي حالات الاستخدام.

\n\n

الخطوة 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\\                

المدونات

توصيات الكتب