NestJS — Node.js للمؤسسات

الوحدات الديناميكية ونطاقات المزوّدات

17 دقيقة الدرس 9 من 48

الوحدات الديناميكية ونطاقات المزوّدات

حتى الآن كانت الوحدات ساكنة — إعدادات ثابتة. لكن كيف تقبل ConfigModule.forRoot() خيارات؟ تلك وحدة ديناميكية: وحدة تُضبَط وقت الاستيراد. ومقترنةً بـ نطاقات المزوّدات، هذه هي الأدوات التي تجعل وحدات NestJS قابلة لإعادة الاستخدام ومرنة.

نمط forRoot / forFeature

رأيت هذا في كل مكان: ConfigModule.forRoot() وTypeOrmModule.forRoot() وJwtModule.register(). كلٌّ دالّة ساكنة تُعيد وحدة مضبوطة على الفور.

import { Module, DynamicModule } from '@nestjs/common'; @Module({}) export class ConfigModule { static forRoot(options: { folder: string }): DynamicModule { return { module: ConfigModule, providers: [ { provide: 'CONFIG_OPTIONS', useValue: options }, ConfigService, ], exports: [ConfigService], }; } }

الآن يمرّر المستهلكون الخيارات عند الاستيراد:

@Module({ imports: [ConfigModule.forRoot({ folder: './config' })], }) export class AppModule {}
العُرف: تضبط forRoot() وحدة مرّة للتطبيق كلّه (الجذر)؛ وتضبط forFeature() شريحة لوحدة ميزة محدّدة. وتُستخدَم register() حين لا يوجد تمييز بين عام وميزة.

نطاقات المزوّدات

افتراضيًا كل مزوّد مفرد — نسخة واحدة مشتركة على مستوى التطبيق. يوفّر NestJS ثلاثة نطاقات:

  • DEFAULT (مفرد) — نسخة واحدة مشتركة. سريع، والخيار الصحيح لكل شيء تقريبًا.
  • REQUEST — نسخة جديدة لكل طلب وارد.
  • TRANSIENT — نسخة جديدة لكل مستهلك يحقنه.
import { Injectable, Scope } from '@nestjs/common'; @Injectable({ scope: Scope.REQUEST }) export class RequestContextService { // تُنشأ نسخة جديدة لكل طلب }

متى تحتاج نطاق REQUEST

نطاق الطلب مفيد حين يجب أن يحمل المزوّد بيانات خاصّة بالطلب الحالي — المستخدم المُصادَق، أو معرّف تتبّع لكل طلب، أو معلومات المستأجر في تطبيق متعدّد المستأجرين. ويستطيع المزوّد ذو نطاق الطلب حقن كائن الطلب نفسه:

import { Injectable, Scope, Inject } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; @Injectable({ scope: Scope.REQUEST }) export class TenantService { constructor(@Inject(REQUEST) private readonly request: any) {} get tenantId() { return this.request.headers['x-tenant-id']; } }

تكلفة نطاق الطلب

النطاق يتصاعد — وله تكلفة أداء. إن حقن متحكّمٌ خدمةً ذات نطاق طلب، صار المتحكّم نفسه ذا نطاق طلب، وكذلك كل ما يعتمد عليه. عندها يجب على NestJS إنشاء تلك السلسلة كاملةً في كل طلب بدل مرّة واحدة عند الإقلاع. استخدم نطاق الطلب فقط حين تحتاج فعلًا حالةً لكل طلب.
غالبًا يوجد بديل أفضل: مرّر بيانات الطلب كوسيط للدالّة، أو اقرأها من سياق AsyncLocalStorage، بدل جعل خدمة كاملة بنطاق طلب. أبقِ المفردات مفردات كلّما أمكن.

الخلاصة

تُعيد الوحدات الديناميكية (نمط forRoot/forFeature/register) كائن DynamicModule مضبوطًا وقت الاستيراد، وهكذا تعمل وحدات المكتبات القابلة للإعداد. وتتحكّم نطاقات المزوّدات في عمر النسخة: المفردات الافتراضية هي القاعدة؛ وتوجد REQUEST وTRANSIENT لحالة الطلب أو المستهلك، لكنها تتصاعد وتكلّف أداءً. بهذا تكتمل المرحلة الثانية — صرت تفهم كيف يربط NestJS ويضبط كل شيء.