تخطَّ إلى المحتوى

🟩 شرح Node.js

الأنماط السلوكية في Node.js

الدرس 28 من 29· ⏱ 5 دقائق قراءة

مقدمة

الأنماط السلوكية (Behavioral Patterns) تنظّم التفاعل بين الكائنات وتوزّع المسؤوليات. في Node.js، هذه الأنماط تظهر بشكل طبيعي في الأطر والمكتبات الأكثر استخدامًا.

Middleware Pattern — نمط الوسيط

في Express

الـ Middleware هو دالة تتمتع بإمكانية الوصول إلى req و res والدالة التالية next:

const express = require('express');
const app = express();

// Middleware للتسجيل
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
});

// Middleware للتحقق من المصادقة
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'مطلوب توثيق' });
  }
  req.user = { id: 1, role: 'admin' };
  next();
};

// Middleware للتحقق من الصلاحيات
const authorize = (...roles) => (req, res, next) => {
  if (!roles.includes(req.user.role)) {
    return res.status(403).json({ error: 'غير مصرح' });
  }
  next();
};

app.get('/admin', authenticate, authorize('admin'), (req, res) => {
  res.json({ message: 'مرحبًا يا أدمن' });
});

بناء Middleware بنفسك

class MiddlewarePipeline {
  constructor() {
    this.middlewares = [];
  }

  use(fn) {
    this.middlewares.push(fn);
    return this;
  }

  execute(context) {
    let index = 0;
    const next = (err) => {
      if (err) {
        console.error('خطأ في middleware:', err);
        return;
      }
      if (index < this.middlewares.length) {
        this.middlewares[index++](context, next);
      }
    };
    next();
  }
}

const pipeline = new MiddlewarePipeline();
pipeline.use((ctx, next) => {
  ctx.data = 'معدّل';
  next();
});
pipeline.use((ctx, next) => {
  console.log(ctx.data);
  next();
});
pipeline.execute({});

Pub/Sub مع EventEmitter

نمط النشر والاشتراك يسمح بفصل المكونات عن بعضها:

const EventEmitter = require('events');

class NotificationService extends EventEmitter {
  sendNotification(type, message) {
    console.log(`إرسال إشعار من نوع: ${type}`);
    this.emit(type, message);
  }
}

const notifier = new NotificationService();

// المشتركون
notifier.on('email', (msg) => {
  console.log(`إرسال بريد: ${msg}`);
});

notifier.on('sms', (msg) => {
  console.log(`إرسال رسالة نصية: ${msg}`);
});

notifier.on('push', (msg) => {
  console.log(`إرسال إشعار push: ${msg}`);
});

notifier.sendNotification('email', 'تم تأكيد طلبك');
notifier.sendNotification('sms', 'رمز التحقق: 1234');

Pub/Sub مع Wildcards

const EventEmitter = require('events');

class WildcardEmitter extends EventEmitter {
  emit(type, ...args) {
    super.emit('*', type, ...args);
    super.emit(type, ...args);
  }
}

const events = new WildcardEmitter();

events.on('*', (type, data) => {
  console.log(`حدث عام - النوع: ${type}`, data);
});

events.on('user:created', (user) => {
  console.log('مستخدم جديد:', user.name);
});

events.emit('user:created', { name: 'عمر', email: 'omar@test.com' });

Strategy Pattern — نمط الإستراتيجية في Express

مصادقة متعددة الاستراتيجيات

class AuthStrategy {
  async authenticate(req) {
    throw new Error('يجب تطبيق هذه الدالة');
  }
}

class JWTStrategy extends AuthStrategy {
  async authenticate(req) {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) throw new Error('مطلوب توكن');
    // فك التشفير والتحقق
    return { id: 1, name: 'خالد', role: 'user' };
  }
}

class APIKeyStrategy extends AuthStrategy {
  async authenticate(req) {
    const key = req.headers['x-api-key'];
    if (key === 'secret-key-123') {
      return { id: 2, name: 'API User', role: 'api' };
    }
    throw new Error('مفتاح API غير صالح');
  }
}

class AuthContext {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  async authenticate(req) {
    return this.strategy.authenticate(req);
  }
}

// الاستخدام
const authContext = new AuthContext(new JWTStrategy());

app.use(async (req, res, next) => {
  try {
    if (req.headers['x-api-key']) {
      authContext.setStrategy(new APIKeyStrategy());
    }
    req.user = await authContext.authenticate(req);
    next();
  } catch (err) {
    res.status(401).json({ error: err.message });
  }
});

Chain of Responsibility — سلسلة المسؤوليات

كل Middleware إما يعالج الطلب أو يمرّره للـ Middleware التالي:

class ValidationHandler {
  constructor() {
    this.handlers = [];
  }

  addHandler(handler) {
    this.handlers.push(handler);
    return this;
  }

  async handle(request) {
    for (const handler of this.handlers) {
      const result = await handler(request);
      if (result !== undefined) return result;
    }
    return null;
  }
}

const validationChain = new ValidationHandler();

validationChain
  .addHandler((req) => {
    if (!req.body.email) return { error: 'البريد الإلكتروني مطلوب' };
  })
  .addHandler((req) => {
    if (req.body.email && !req.body.email.includes('@')) {
      return { error: 'بريد إلكتروني غير صالح' };
    }
  })
  .addHandler((req) => {
    if (!req.body.password || req.body.password.length < 6) {
      return { error: 'كلمة المرور قصيرة جدًا' };
    }
  });

app.post('/register', async (req, res) => {
  const error = await validationChain.handle(req);
  if (error) return res.status(400).json(error);
  res.json({ message: 'تم التحقق بنجاح' });
});

Observer Pattern — المراقب

نمط Observer في Node.js مبني على EventEmitter:

class UserRepository extends EventEmitter {
  constructor() {
    super();
    this.setMaxListeners(50);
  }

  async create(userData) {
    const user = { id: Date.now(), ...userData, createdAt: new Date() };
    this.emit('user:beforeCreate', user);
    // حفظ في قاعدة البيانات
    this.emit('user:created', user);
    this.emit('user:afterCreate', user);
    return user;
  }

  async delete(userId) {
    this.emit('user:deleting', userId);
    // حذف من قاعدة البيانات
    this.emit('user:deleted', userId);
  }
}

const userRepo = new UserRepository();

// مراقبين مستقلين
userRepo.on('user:created', async (user) => {
  await emailService.sendWelcome(user.email);
});

userRepo.on('user:created', async (user) => {
  await analyticsService.trackEvent('user_signup', user);
});

userRepo.on('user:deleted', async (userId) => {
  await cacheService.invalidate(`user:${userId}`);
});

State Management Patterns — أنماط إدارة الحالة

Finite State Machine

class OrderStateMachine {
  constructor() {
    this.states = {
      pending: { next: ['confirmed', 'cancelled'] },
      confirmed: { next: ['shipped', 'cancelled'] },
      shipped: { next: ['delivered', 'returned'] },
      delivered: { next: ['returned'] },
      cancelled: { next: [] },
      returned: { next: [] },
    };
    this.currentState = 'pending';
    this.listeners = new Map();
  }

  canTransition(toState) {
    return this.states[this.currentState].next.includes(toState);
  }

  transition(toState, data = {}) {
    if (!this.canTransition(toState)) {
      throw new Error(
        `لا يمكن الانتقال من ${this.currentState} إلى ${toState}`
      );
    }
    const fromState = this.currentState;
    this.currentState = toState;
    this._emit('transition', { fromState, toState, data });
  }

  on(event, callback) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(callback);
  }

  _emit(event, data) {
    const callbacks = this.listeners.get(event) || [];
    callbacks.forEach(cb => cb(data));
  }
}

const order = new OrderStateMachine();
order.on('transition', ({ fromState, toState }) => {
  console.log(`طلب: ${fromState}${toState}`);
});
order.transition('confirmed');
order.transition('shipped');

⚠️ تحذير: الأنماط السلوكية تزيد من مرونة الكود لكنها قد تزيد أيضًا من التعقيد. استخدمها فقط عندما تكون هناك حاجة حقيقية للفصل بين المكونات.

خلاصة

  • Middleware: يفصل معالجة الطلب إلى دوال صغيرة متسلسلة.
  • Pub/Sub: يسمح بالتواصل غير المباشر بين المكونات عبر EventEmitter.
  • Strategy: يوفّر خوارزميات قابلة للتبديل (مثل استراتيجيات المصادقة).
  • Chain of Responsibility: يمرّر الطلب عبر سلسلة من المعالجات.
  • Observer: يوفّر نموذج حدث-مستمع لفصل المكونات.
  • State Machine: يدير التحولات بين الحالات بشكل واضح وآمن.

🎯 التالي: العمارة المعتمدة على الأحداث

شرح الأنماط السلوكية في Node.js — Node.js بالعربي
الأنماط السلوكية في Node.jsNode.js بالعربي · The Code Fix

📚 لمزيد من التعمّق في Node.js، راجِع التوثيق الرسمي لـ Node.js.

هل كان هذا الدرس مفيدًا؟