Modules — Feature, Shared, Global, Dynamic

~20 daq · NestJS · Boshlang'ich daraja

Loyiha o'sib boradi

Siz kichik loyiha boshladingiz. Avvaliga hammasi AppModule da edi — bir controller, bir service. Ishladi. Lekin loyiha o'sdi — foydalanuvchilar, mahsulotlar, buyurtmalar, to'lovlar paydo bo'ldi. Hammasi bitta modulda — bu endi boshqarib bo'lmaydigan darajaga keldi.

Aynan shu paytda Module tizimi qutqaradi.

NestJS da modul — bu loyihaning bir bo'lagi. Har bir bo'lak o'z ichida yopiq, o'z vazifasiga ega. Boshqa bo'laklar bilan faqat kerak bo'lganda bog'lanadi.


Modul qanday ko'rinadi?

import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";

@Module({
  imports: [], // boshqa modullardan nima kerak
  controllers: [], // bu modulning controllerlari
  providers: [], // bu modulning servicelari
  exports: [], // boshqa modullarga nima berish
})
export class UsersModule {}

@Module decorator to'rtta maydon qabul qiladi. Hozircha ularni yodlab olmang — ish jarayonida o'zlari yodlanadi.


Feature Module — eng ko'p ishlatiladigan tur

Feature Module — bu loyihaning bitta funksiyasiga bag'ishlangan modul. "Foydalanuvchilar" funksiyasi — UsersModule. "Mahsulotlar" funksiyasi — ProductsModule.

Keling, to'liq misol ko'ramiz. Faraz qiling, e-commerce loyiha yozyapsiz:

src/
├── users/
│   ├── users.module.ts
│   ├── users.controller.ts
│   └── users.service.ts
├── products/
│   ├── products.module.ts
│   ├── products.controller.ts
│   └── products.service.ts
├── orders/
│   ├── orders.module.ts
│   ├── orders.controller.ts
│   └── orders.service.ts
└── app.module.ts

Har bir papka — alohida dunyo. users papkasidagi fayllar faqat foydalanuvchilar bilan ishlaydi. products — faqat mahsulotlar bilan. Tartib bor, aniqlik bor.

CLI bilan yaratish:

nest g mo users          # users.module.ts
nest g co users --no-spec  # users.controller.ts
nest g s users --no-spec   # users.service.ts

--no-spec — test fayllarini yaratmaslik uchun. Har bir buyruq faylni yaratib, users.module.ts ga avtomatik ulaydi.


Modullarni bog'lash — imports va exports

Endi amaliy holat: OrdersService buyurtma yaratayotganda foydalanuvchini tekshirishi kerak. Buning uchun UsersService ga murojaat qilishi lozim.

Lekin UsersService — UsersModule ichida. OrdersModule uni ko'rmaydi. Qanday hal qilamiz?

1-qadam: UsersModule da UsersService ni export qilamiz:

// users/users.module.ts
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";

@Module({
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService], // ← boshqalarga ochib beramiz
})
export class UsersModule {}

2-qadam: OrdersModule da UsersModule ni import qilamiz:

// orders/orders.module.ts
import { Module } from "@nestjs/common";
import { OrdersController } from "./orders.controller";
import { OrdersService } from "./orders.service";
import { UsersModule } from "../users/users.module";

@Module({
  imports: [UsersModule], // ← UsersModule ni olib kelamiz
  controllers: [OrdersController],
  providers: [OrdersService],
})
export class OrdersModule {}

3-qadam: Endi OrdersService UsersService ni erkin ishlatishi mumkin:

// orders/orders.service.ts
import { Injectable } from "@nestjs/common";
import { UsersService } from "../users/users.service";

@Injectable()
export class OrdersService {
  constructor(private readonly usersService: UsersService) {}

  create(userId: number, productId: number) {
    const user = this.usersService.findOne(userId);

    if (!user) {
      return { error: "Foydalanuvchi topilmadi" };
    }

    return {
      orderId: Math.random(),
      user: user.name,
      productId,
      status: "yaratildi",
    };
  }
}

💡 exports ga yozmagan Service — faqat o'z moduli ichida ko'rinadi. Bu yaxshi narsa — keraksiz narsalarni yashirasiz, faqat keraklilarni ochasiz.


Shared Module — umumiy modullar

Ba'zi narsalar butun loyiha bo'ylab kerak bo'ladi. Masalan, LoggerService — hamma joyda loglash kerak. HelperService — umumiy yordamchi funksiyalar.

Buning uchun Shared Module yaratiladi:

// shared/shared.module.ts
import { Module } from "@nestjs/common";
import { LoggerService } from "./logger.service";
import { HelperService } from "./helper.service";

@Module({
  providers: [LoggerService, HelperService],
  exports: [LoggerService, HelperService], // hammaga ochiq
})
export class SharedModule {}

Keyin har bir modulda import qilinadi:

// users/users.module.ts
import { SharedModule } from "../shared/shared.module";

@Module({
  imports: [SharedModule],
  // ...
})
export class UsersModule {}

Endi UsersService, ProductsService, OrdersService — barchasi LoggerService va HelperService ni ishlatishi mumkin.


Global Module — hamma joyda avtomatik

SharedModule ni har bir modulga alohida import qilish — zerikarli. Agar 20 ta modul bo'lsa, 20 ta joyga yozish kerakmi?

Buning uchun Global Module bor:

// shared/shared.module.ts
import { Global, Module } from "@nestjs/common";
import { LoggerService } from "./logger.service";

@Global() // ← shu decorator yetarli
@Module({
  providers: [LoggerService],
  exports: [LoggerService],
})
export class SharedModule {}

@Global() decorator qo'yilgandan keyin — bu modulni faqat AppModule da bir marta import qilasiz:

// app.module.ts
import { Module } from "@nestjs/common";
import { SharedModule } from "./shared/shared.module";
import { UsersModule } from "./users/users.module";
import { ProductsModule } from "./products/products.module";

@Module({
  imports: [
    SharedModule, // bir marta — hammaga yetadi
    UsersModule,
    ProductsModule,
  ],
})
export class AppModule {}

Endi UsersModule, ProductsModule va boshqa barcha modullarda LoggerService import qilmasdan ishlatish mumkin.

⚠️ @Global() ni ehtiyotkorlik bilan ishlating. Hamma narsani global qilib qo'ysangiz — modullar orasidagi bog'liqlikni kuzatish qiyinlashadi. Faqat haqiqatan hamma joyda kerakli narsalargina global bo'lsin.


Dynamic Module — moslashuvchan modullar

Ba'zan modul yaratilayotganda unga sozlamalar berish kerak bo'ladi. Masalan, ma'lumotlar bazasi moduli — ulanish ma'lumotlarini olishi kerak. Email moduli — SMTP server ma'lumotlarini olishi kerak.

Buning uchun Dynamic Module ishlatiladi:

// database/database.module.ts
import { Module, DynamicModule } from "@nestjs/common";
import { DatabaseService } from "./database.service";

@Module({})
export class DatabaseModule {
  static forRoot(options: {
    host: string;
    port: number;
    database: string;
  }): DynamicModule {
    return {
      module: DatabaseModule,
      providers: [
        {
          provide: "DB_OPTIONS", // token — nom sifatida
          useValue: options, // qiymat
        },
        DatabaseService,
      ],
      exports: [DatabaseService],
    };
  }
}

AppModule da ishlatish:

// app.module.ts
@Module({
  imports: [
    DatabaseModule.forRoot({
      host: "localhost",
      port: 5432,
      database: "mydb",
    }),
  ],
})
export class AppModule {}

DatabaseService ichida bu sozlamalarni olish:

// database/database.service.ts
import { Injectable, Inject } from "@nestjs/common";

@Injectable()
export class DatabaseService {
  constructor(
    @Inject("DB_OPTIONS")
    private options: {
      host: string;
      port: number;
      database: string;
    },
  ) {
    console.log(
      `${options.host}:${options.port}/${options.database} ga ulanmoqda`,
    );
  }
}

💡 Real loyihalarda siz DatabaseModule.forRoot() ni o'zingiz yozmaysiz — TypeORM, Mongoose, Prisma kabi kutubxonalar bu pattern ni tayyor holda beradi. Lekin ichida qanday ishlashini tushunish muhim.


AppModule — bosh modul

Barcha modullar oxir-oqibat AppModule ga ulanadi. U loyihaning "ildizi":

// app.module.ts
import { Module } from "@nestjs/common";
import { UsersModule } from "./users/users.module";
import { ProductsModule } from "./products/products.module";
import { OrdersModule } from "./orders/orders.module";
import { SharedModule } from "./shared/shared.module";

@Module({
  imports: [SharedModule, UsersModule, ProductsModule, OrdersModule],
})
export class AppModule {}

AppModule da controller va provider kamdan-kam yoziladi — u faqat modullarni birlashtiradi.


To'liq ko'rinish

AppModule
├── SharedModule (Global)
│   └── LoggerService → hamma joyda ishlatiladi
├── UsersModule
│   ├── UsersController
│   └── UsersService → export qilingan
├── ProductsModule
│   ├── ProductsController
│   └── ProductsService
└── OrdersModule
    ├── OrdersController
    ├── OrdersService → UsersService ni ishlatadi
    └── imports: [UsersModule]

Har bir modul o'z ichida yopiq. Faqat exports orqali ochilgan narsalar tashqaridan ko'rinadi.


Xulosa

Module — bu NestJS loyihasining qurilish g'ishtlari. Har bir g'isht o'z ichida mustaqil, lekin boshqalari bilan bog'lanishi mumkin.

Eslab qoling:

  • Feature Module → bitta funksiya uchun (UsersModule, ProductsModule)
  • Shared Module → umumiy narsalar uchun, exports bilan ochiladi
  • Global Module → @Global() — bir marta import, hamma joyda ishlatish
  • Dynamic Module → sozlamalar bilan yaratilgan modul (forRoot())
  • imports → boshqa moduldan foydalanish
  • exports → o'z ichidagi narsani boshqalarga berish

Keyingi dars

5-dars: DTO + Class-validator + Class-transformer