الهندسة النظيفة في JavaScript: إتقان Hexagonal عمليًا

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

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

ابدأ الدورة

كودك هو حصن: لماذا ستنقذ هندسة Hexagonal مشروع JavaScript الخاص بك

\n\n

هل سبق لك أن واجهت موقفًا حيث تحول تغيير قاعدة البيانات أو استبدال API خارجي إلى جحيم من إعادة كتابة نصف المشروع؟ أو ربما كنت تكتب اختبارات وحدة تشبه أكثر اختبارات التكامل، لأن منطق الأعمال كان مرتبطًا بإحكام بـ Express أو MongoDB؟ إذا كان الأمر كذلك - مرحبًا بك في عالم هندسة Hexagonal (Ports and Adapters).

\n\n

هذا ليس مجرد نمط عصري من كتب أليستر كوبرن. إنها طريقة عملية لجعل كود JavaScript الخاص بك قويًا، قابلًا للاختبار، ومستقلاً عن الأطر. في هذه المقالة، لن نلقي محاضرات مملة. سنأخذ مهمة حقيقية (نظام إدارة الطلبات) ونبنيها خطوة بخطوة على مبادئ الهندسة السداسية في JavaScript. سترى كيف يظل منطق الأعمال نقيًا، بينما تبقى كل "الضوضاء" (قواعد البيانات، طلبات HTTP، قوائم الانتظار) بالخارج.

\n\n

هل أنت مستعد؟ لننطلق!

\n\n

ما هي هندسة Hexagonal (وما علاقة المنافذ والمهايئات بها)؟

\n\n

تخيل بوابة (gateway) لسفينة فضائية. بالداخل - لوحة التحكم (منطق أعمالك). بالخارج - عدد لا نهائي من الكواكب المختلفة (قواعد البيانات، أطر الويب، الخدمات الخارجية). البوابة هي المنفذ (الواجهة). وبدلة الفضاء التي تسمح بالتنفس على كوكب معين هي المهايئ.

\n\n

الفكرة الأساسية: نواة التطبيق (core) لا يجب أن تعرف من وكيف يطرق بابها. إنها تعلن فقط عن "منافذ" (واجهات). بينما يتم توصيل التطبيقات المحددة (المهايئات) من الخارج. هذا يمنح ثلاث قدرات خارقة:

\n\n
    \n
  • عزل منطق الأعمال - تغير قاعدة البيانات دون لمس كود حساب الخصومات.
  • \n
  • قابلية الاختبار - تختبر المنطق دون تشغيل Docker وقاعدة بيانات حقيقية.
  • \n
  • الاستقلال عن الإطار - يمكنك الانتقال من Express إلى Fastify بمجرد كتابة مهايئ جديد.
  • \n
\n\n

دعنا ننتقل إلى الكود.

\n\n

إعداد المشروع وهيكل المجلدات

\n\n

في البداية، لننشئ هيكلًا بسيطًا. سنكتب بلغة TypeScript النقية (أو JavaScript مع JSDoc - حسب ما يناسبك). هدفنا هو نظام إدارة الطلبات.

\n\n
src/\n├── core/\n│   ├── domain/          # الكيانات وكائنات القيمة\n│   ├── ports/           # الواجهات (المنافذ)\n│   └── services/        # منطق الأعمال (حالات الاستخدام)\n├── adapters/\n│   ├── primary/         # المهايئات التي تستدعي نواتنا (HTTP, CLI)\n│   └── secondary/       # المهايئات التي تستدعيها النواة (قاعدة بيانات، APIs خارجية)\n└── app.js               # نقطة الدخول (تجميع كل شيء معًا)\n
\n\n

الخطوة 1. النواة: الكيانات المجالية والمنافذ

\n\n

لنبدأ بالأهم - بما لا يعتمد على أي شيء. لننشئ كيان Order (طلب).

\n\n

core/domain/Order.js

\n\n
export class Order {\n  constructor({ id, userId, items, status = 'pending' }) {\n    this.id = id;\n    this.userId = userId;\n    this.items = items; // مصفوفة { productId, quantity, price }\n    this.status = status;\n    this.total = this.calculateTotal();\n  }\n\n  calculateTotal() {\n    return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n  }\n\n  approve() {\n    if (this.status !== 'pending') {\n      throw new Error('Only pending orders can be approved');\n    }\n    this.status = 'approved';\n  }\n}\n
\n\n

الآن لنحدد المنافذ - الواجهات التي تتوقعها نواتنا من العالم الخارجي.

\n\n

core/ports/OrderRepository.js

\n\n
// هذا منفذ (واجهة). لا ننفذه هنا.\nexport class OrderRepository {\n  async save(order) { throw new Error('Not implemented'); }\n  async findById(id) { throw new Error('Not implemented'); }\n}\n
\n\n

core/ports/EmailService.js

\n\n
export class EmailService {\n  async sendOrderConfirmation(order) { throw new Err                

المدونات

توصيات الكتب