Awilix مع TypeScript: دليل شامل لحاوية حقن التبعيات (DI)
Awilix هي حاوية حقن تبعيات (Dependency Injection - DI) قوية ومرنة لتطبيقات Node.js والمتصفحات، مكتوبة بلغة TypeScript. توفر طريقة أنيقة لإدارة التبعيات في تطبيقك، مما يجعل الكود أكثر نمطية (modular)، وقابلية للاختبار (testable)، وسهولة في الصيانة. في هذا المقال، سنشرح بالتفصيل كيفية استخدام Awilix في مشاريع TypeScript، بدءًا من التثبيت وصولاً إلى التقنيات المتقدمة.
1. ما هي Awilix ولماذا نحتاجها؟
في تطوير البرمجيات الحديث، أصبح مبدأ عكس التحكم (IoC) وحقن التبعيات (DI) المعيار الفعلي لبناء تطبيقات قابلة للتوسع والاختبار. Awilix هي تطبيق لحاوية DI تتولى إنشاء وإدارة دورة حياة الكائنات (الخدمات، المستودعات، المتحكمات، إلخ). بدلاً من إنشاء نسخ من الكلاسات يدويًا داخل كلاسات أخرى (مما يؤدي إلى ارتباط وثيق)، تقوم بتسجيل تبعياتك في الحاوية، وهي تقوم تلقائيًا بـ "حقنها" في الأماكن التي تحتاجها.
لماذا نحتاجها؟ أولاً، تبسط عملية الاختبار بشكل جذري. يمكنك بسهولة استبدال التنفيذ الفعلي (مثل قاعدة البيانات) بكائن وهمي (mock) أثناء اختبارات الوحدة، فقط عن طريق تسجيل تبعية مختلفة في الحاوية. ثانيًا، تحسن بنية التطبيق، مما يدفعك لاتباع مبدأ المسؤولية الواحدة (SRP) والإعلان عن تبعياتك بوضوح. ثالثًا، تدعم Awilix أنماط دورة حياة مختلفة للكائنات (مفرد - singleton، مؤقت - transient، نطاق - scope)، مما يتيح إدارة مرنة للذاكرة والحالة.
تتميز Awilix بشكل خاص مع TypeScript بفضل كتابتها الصارمة (strict typing). توفر أنواعًا مدمجة لتسجيل وحل التبعيات، مما يسمح باكتشاف الأخطاء في مرحلة الترجمة (compile-time) بدلاً من وقت التشغيل (runtime). تدعم المكتبة حل التبعيات غير المتزامن (async)، والعمل مع الكلاسات، ودوال المصانع (factory functions)، والنسخ الموجودة مسبقًا من الكائنات.
2. التثبيت
لتثبيت Awilix في مشروع TypeScript الخاص بك، قم بتنفيذ الأمر التالي في الطرفية (terminal):
npm install awilix
Awilix تدعم TypeScript بشكل مدمج، لذا لا تحتاج إلى تثبيت حزم منفصلة للأنواع (مثل @types/awilix). جميع الأنواع مضمنة بالفعل في المكتبة الأساسية.
3. البداية السريعة — مثال عملي بسيط
لنقم بإنشاء تطبيق بسيط يرحب بالمستخدم. سيكون لدينا كلاس Greeter يعتمد على Logger.
import { createContainer, asClass, Lifetime } from 'awilix';
// 1. تعريف التبعيات
class Logger {
log(message: string): void {
console.log(`[LOG]: ${message}`);
}
}
class Greeter {
private logger: Logger;
// سيتم حقن التبعية عبر المُنشئ (constructor)
constructor({ logger }: { logger: Logger }) {
this.logger = logger;
}
greet(name: string): void {
this.logger.log(`مرحبًا، ${name}!`);
}
}
// 2. إنشاء الحاوية
const container = createContainer();
// 3. تسجيل التبعيات
container.register({
logger: asClass(Logger, { lifetime: Lifetime.SINGLETON }),
greeter: asClass(Greeter, { lifetime: Lifetime.TRANSIENT }),
});
// 4. حل (الحصول على) نسخة من Greeter
const greeter = container.resolve<Greeter>('greeter');
// 5. الاستخدام
greeter.greet('العالم'); // الإخراج: [LOG]: مرحبًا، العالم!
في هذا المثال، قمنا بإنشاء حاوية، وسجلنا كلاسين (Logger كمفرد و Greeter ككائن مؤقت)، ثم حللنا Greeter. قامت Awilix تلقائيًا بإنشاء نسخة من Logger وتمريرها إلى مُنشئ Greeter.
4. الطرق/الدوال/الكلاسات الأساسية
دعنا نستعرض واجهات برمجة التطبيقات (API) الرئيسية لـ Awilix. لكل طريقة، سنقدم التوقيع (signature)، والوصف، ومثال.
4.1. createContainer()
التوقيع: