حاوية حقن التبعيات لجافا سكريبت (Node.js): Awilix
\n\nفي التطوير الحديث على Node.js، يعد الحفاظ على نقاء البنية وقابلية اختبار الكود أمرًا بالغ الأهمية. أحد الأنماط الرئيسية التي تساعد في ذلك هو حقن التبعيات (DI). لتنفيذ هذا النمط بفعالية في بيئة جافا سكريبت، تم إنشاء مكتبات متخصصة - حاويات حقن التبعيات. في هذه المقالة، سنستعرض بالتفصيل واحدة من أشهر المكتبات وأخفها وزنًا - Awilix.
\n\nما هو حقن التبعيات ولماذا نحتاج إلى حاوية؟
\n\nفي النهج التقليدي، يقوم الكائن بإنشاء تبعياته بنفسه (على سبيل المثال، باستخدام عامل التشغيل new). يؤدي هذا إلى ارتباط قوي في الكود، مما يعقد الاختبار والتعديل. يقترح نمط حقن التبعيات تمرير (حقن) التبعيات من الخارج. أما الحاوية فتقوم بأتمتة هذه العملية: فهي تعرف كيفية إنشاء الكائنات وتبعياتها، وتوفر نسخًا جاهزة عند الطلب.
Awilix: حاوية DI خفيفة الوزن وقوية
\n\nAwilix هي مكتبة لـ جافا سكريبت (Node.js) تنفذ نمط عكس التحكم (IoC) باستخدام حاوية. تم كتابتها من الصفر لـ Node.js، وتدعم حل التبعيات غير المتزامن، وأنماط تسجيل مختلفة (فئات، مصانع، قيم)، والكشف التلقائي عن التبعيات عبر المُنشئ.
\n\nالميزات الرئيسية:
\n- \n
- الحقن التلقائي: تقوم الحاوية بتحليل معاملات المُنشئ أو الدالة وحقن التبعيات المطلوبة تلقائيًا. \n
- دعم عدم التزامن: يمكن أن تكون التبعيات غير متزامنة (مثل الاتصال بقاعدة البيانات)، وتنتظر الحاوية حلها بشكل صحيح. \n
- أنماط تسجيل متنوعة: يمكنك تسجيل الفئات (
asClass)، دوال المصنع (asFunction)، أو القيم الجاهزة (asValue). \n - النطاقات (Scopes): يتم دعم دورات حياة مختلفة للكائنات (singleton، transient، scoped). \n
- النمطية: يمكن تقسيم الحاوية بسهولة إلى وحدات وتسجيل التبعيات في أجزاء مختلفة من التطبيق. \n
التثبيت والاستخدام الأساسي
\n\nيتم تثبيت Awilix عبر npm:
\n\nnpm install awilix\n\nلنلقِ نظرة على مثال بسيط. لنفترض أن لدينا خدمة للعمل مع المستخدمين تعتمد على مسجل (logger) ومستودع (repository).
\n\nconst { createContainer, asClass, asFunction, asValue } = require('awilix');\n\n// 1. تعريف التبعيات\nclass Logger {\n log(message) {\n console.log(`[LOG]: ${message}`);\n }\n}\n\nclass UserRepository {\n constructor({ logger }) {\n this.logger = logger;\n }\n\n findById(id) {\n this.logger.log(`البحث عن مستخدم بالمعرف: ${id}`);\n return { id, name: 'John Doe' };\n }\n}\n\nclass UserService {\n constructor({ userRepository, logger }) {\n this.userRepository = userRepository;\n this.logger = logger;\n }\n\n getUser(id) {\n this.logger.log('طلب في UserService');\n return this.userRepository.findById(id);\n }\n}\n\n// 2. إنشاء الحاوية وتسجيل التبعيات\nconst container = createContainer();\n\ncontainer.register({\n logger: asClass(Logger).singleton(),\n userRepository: asClass(UserRepository).singleton(),\n userService: asClass(UserService).singleton()\n});\n\n// 3. استخدام الحاوية للحصول على الخدمة\nconst userService = container.resolve('userService');\nconst user = userService.getUser(1);\nconsole.log(user);\n// الإخراج:\n// [LOG]: طلب في UserService\n// [LOG]: البحث عن مستخدم بالمعرف: 1\n// { id: 1, name: 'John Doe' }\n<