هندسة Event-driven باستخدام TypeScript: عندما "يسمع" الكود بعضه البعض
\n\nتخيل أنك تكتب تطبيق ويب معقد: برنامج مراسلة، بوت تداول، أو نظام مراقبة خوادم. في مرحلة ما، تدرك أن مجرد استدعاء الدوال في سلسلة هو جحيم. وحدة واحدة تسحب أخرى، والثالثة مرتبطة بالرابعة، وعند تغيير المنطق ينهار كل شيء مثل بيت من ورق.
\n\nهل هذا مألوف؟ إذن أنت بحاجة إلى هندسة Event-driven باستخدام TypeScript. هذا ليس مجرد مصطلح عصري، بل طريقة حقيقية لجعل الكود مرنًا وقابلاً للتوسع وواضحًا. في هذه المقالة، سنشرح كيف تعمل البرمجة الموجهة بالأحداث، ونكتب ناقل الأحداث الخاص بنا باستخدام TypeScript، ونرى أين يكون هذا مفيدًا حقًا.
\n\nما هي هندسة Event-driven بكلمات بسيطة
\n\nهندسة Event-driven (الهندسة الموجهة بالأحداث) هي نهج تتواصل فيه مكونات النظام عبر الأحداث. وحدة واحدة تولد حدثًا (مثل "قام المستخدم بالتسجيل")، وتشترك وحدات أخرى فيه وتستجيب له. لا ترتبط هذه الوحدات ببعضها البعض بشكل مباشر.
\n\nيبدو هذا مجردًا؟ دعنا نأخذ مثالاً من الحياة الواقعية. تخيل مكتبًا:
\n- \n
- النهج التقليدي: المدير يذهب شخصيًا إلى كل موظف ويقول: "أعد التقرير". \n
- Event-driven: المدير يصرخ: "التقرير مطلوب!" - وكل من يعرف كيفية إعداده يبدأ العمل بنفسه. \n
في الحالة الثانية، لا يحتاج المدير إلى معرفة من سيقوم بإعداد التقرير بالضبط. هو فقط يرسل الحدث. ويمكن للموظفين الانضمام والانسحاب دون تغيير منطق المدير. هذا هو جوهر البرمجة الموجهة بالأحداث.
\n\nلماذا يعتبر TypeScript مثاليًا للهندسة القائمة على الأحداث
\n\nJavaScript بحد ذاته موجه بالأحداث (تذكر addEventListener)، ولكن في المشاريع الكبيرة بدون أنواع يبدأ الفوضى. أحداث TypeScript تحل هذه المشكلة: أنت تعرف بالضبط البيانات التي تأتي مع الحدث، وسيقوم المترجم بالتقاط الخطأ في مرحلة التطوير وليس في وقت التشغيل.
بالإضافة إلى ذلك، يدعم TypeScript الأدوية العامة (Generics)، والتحميل الزائد (Overloads)، والكتابة الصارمة لدوال الاسترجاع (Callbacks) - كل ما هو مطلوب لإنشاء ناقل أحداث موثوق.
\n\nكتابة Event Bus باستخدام TypeScript من الصفر
\n\nلنبدأ بمثال بسيط ولكنه عملي. سننشئ كلاس EventBus - ناقل أحداث مركزي تتواصل من خلاله جميع الوحدات.
// event-bus.ts\ntype EventHandler<T = any> = (data: T) => void;\n\nclass EventBus {\n private handlers: Map<string, EventHandler[]> = new Map();\n\n // الاشتراك في الحدث\n on<T>(event: string, handler: EventHandler<T>): void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, []);\n }\n this.handlers.get(event)!.push(handler as EventHandler);\n }\n\n // إلغاء الاشتراك من الحدث\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n this.handlers.set(event, handlers.filter(h => h !== handler));\n }\n }\n\n // إصدار الحدث\n emit<T>(event: string, data: T): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(data));\n }\n }\n}\n\nexport const globalEventBus = new EventBus();\n\nهكذا قمنا بكتابة هندسة Event-driven بسيطة باستخدام TypeScript. الآن يمكن لأي وحدة استيراد globalEventBus وإما الاستماع إلى الأحداث أو توليدها.
مثال على الاستخدام: الإشعارات وتسجيل الأحداث
\n\nلنفترض أن لدينا وحدة مصادقة ووحدة إشعارات. لا ينبغي لهما معرفة بعضهما البعض بشكل مباشر.
\n\n// auth-module.ts\nimport { globalEventBus } from './event-bus';\n\nexport function loginUser(email: string): void {\n // ... منطق تسجيل الدخول\n console.log(`المستخدم