WisarWisar
Dasturlash kitobi/8-QISM — NestJS25 daqiqa

8.16-bob: Microservices — NestJS mikroservislar

8-QISM — NestJS (chuqur) · 16-mavzu


1. Kirish va motivatsiya

Production poydevorini 8.15-bob bildik. Endi NestJS'ning ilg'or arxitektura mavzusiga — mikroservislar ga — o'tamiz. Shu paytgacha monolit (yaxlit ilova — barcha modul bitta jarayonda) qurdik. Mikroservis — ilovani mustaqil, kichik xizmatlarga bo'lish (har biri o'z vazifasi, o'z DB'si, alohida deploy). Bu — katta, masshtablanadigan tizimlar (Netflix, Uber, Amazon) arxitekturasi. NestJS bu mavzuda ham kuchli — @nestjs/microservices paketi TCP, Redis, RabbitMQ, Kafka, gRPC, NATS transport'larini bir xil kod bilan beradi.

Avval muhim haqiqat: mikroservis — har doim to'g'ri tanlov emas. Ko'p loyiha uchun monolit yetarli (oddiyroq, tezroq). Mikroservis murakkablik qo'shadi (tarmoq, taqsimlangan tranzaksiya, monitoring). Lekin katta jamoa, mustaqil masshtab (bir qism og'ir yuklangan), turli texnologiya kerak bo'lganda — mikroservis. Bu bobda ikkalasini ham ko'rib chiqamiz: qachon monolit, qachon mikroservis, va NestJS'da mikroservis qanday quriladi.

Bu bob: monolit vs mikroservis (qachon qaysi), NestJS transport'lar (TCP/Redis/RabbitMQ/Kafka/gRPC), message pattern (so'rov-javob) vs event pattern (fire-and-forget), ClientProxy (xizmatlararo aloqa), API Gateway, hybrid app, va amaliy naqshlar — chuqur. Bu bob 9-QISM (arxitektura) va 10-QISM (DevOps) bilan bog'liq. Bu — senior darajadagi arxitektura bilimi.

O'xshatish: monolit — bitta katta restoran (oshpaz, ofitsiant, kassir — bir bino, bir jamoa — oson boshqarish, lekin kengaytirish qiyin). Mikroservis — ko'cha taomlari bozori (har kiosk mustaqil — biri lavash, biri choy, biri shashlik — har biri alohida ishlaydi, alohida kengayadi, biri yopilsa boshqasi ishlaydi). Kioskar bir-biri bilan buyurtma chiplari orqali gaplashadi (message/event — transport). API Gateway — bozor kirishidagi yagona ma'lumot punkti (mijoz bitta joyga boradi, u kerakli kioskka yo'naltiradi). Bozor — moslashuvchan, lekin boshqarish murakkab (monolit restorandan ko'ra).

Nega muhim?

  • Masshtab — har xizmat mustaqil kengayadi (katta tizim).
  • Mustaqil deploy — bir xizmat o'zgarsa, boshqasi tegmaydi.
  • Senior bilim — arxitektura tanlovi (monolit vs mikroservis).
  • NestJS kuchi — transport-agnostik (bir kod, ko'p transport).

2. Nazariya — chuqur tushuntirish

2.1. Monolit vs Mikroservis (qachon qaysi)

text
  MONOLIT (bitta ilova):
   Oddiy (bir kod, bir deploy, bir DB)
   Tez boshlash (ko'p loyiha uchun yetarli)
   Oson debug/test (bir jarayon)
   Katta bo'lsa — og'ir; butun masshtab; bir xato  hammasi

  MIKROSERVIS (ko'p mustaqil xizmat):
   Mustaqil masshtab (faqat og'ir qism)
   Mustaqil deploy/texnologiya; xato izolyatsiyasi
   Murakkab (tarmoq, taqsimlangan tranzaksiya, monitoring)
   Operatsion og'irlik (ko'p deploy, kuzatuv)

  QOIDA: Monolit'dan boshlang  kerak bo'lganda ajrating ("monolith first")

Monolit vs Mikroservis (eng muhim qaror): monolit — oddiy, ko'p loyiha uchun yetarli (boshlash uchun ideal); mikroservis — katta jamoa/masshtab/mustaqillik kerak bo'lganda. "Monolith first" (Martin Fowler) — monolitdan boshlang, o'sganda ajrating. Mikroservisni erta tanlash — keraksiz murakkablik. Bu — senior arxitektura mulohazasi (9-QISM).

2.2. NestJS mikroservis asosi

ts
// Mikroservis yaratish (HTTP emas — transport)
import { NestFactory } from "@nestjs/core";
import { Transport } from "@nestjs/microservices";

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    transport: Transport.TCP,                         // transport (2.4)
    options: { host: "0.0.0.0", port: 3001 },
  });
  await app.listen();
}
bootstrap();

NestJS mikroserviscreateMicroservice (HTTP server emas, transport tinglovchi). Transport (TCP/Redis/...). Mikroservis — controller o'rniga message handler (@MessagePattern — 2.5). Bir xil NestJS (modul, DI, service) — faqat kirish HTTP emas, message. Bu — @nestjs/microservices asosi.

2.3. Transport'lar (taqqoslash — muhim jadval)

text
  ┌──────────┬─────────────────────────────────────────────┐
  │ TCP      │ Eng oddiy (tug'ma). O'rganish uchun. Kichik. │
  │ Redis    │ Pub/Sub. Yengil. Tez. (5.21 Redis)          │
  │ RabbitMQ │ Navbat (queue). Routing, dead-letter, retry. │
  │          │ Vazifa qayta ishlash uchun (task)            │
  │ Kafka    │ Event streaming. Durable, replay, partition. │
  │          │ Event manba (event sourcing). Og'ir.         │
  │ gRPC     │ Tez, tipli (protobuf). Xizmatlararo RPC.     │
  │ NATS     │ Yengil, tez pub/sub.                          │
  └──────────┴─────────────────────────────────────────────┘

Transport tanlovi: TCP (oddiy — o'rganish), Redis (yengil pub/sub — 5.21), RabbitMQ (navbat — vazifa, routing, retry, dead-letter), Kafka (event streaming — durable, replay — event manba bo'lsa), gRPC (tez, tipli — xizmatlararo RPC). NestJS — bir xil kod, transport almashtirsa kod o'zgarmaydi (abstraksiya). Tanlov ehtiyojga bog'liq.

2.4. Transport sozlash (Redis misol)

ts
// Redis transport
const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.REDIS,
  options: { host: "localhost", port: 6379 },
});

// RabbitMQ transport
const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.RMQ,
  options: { urls: ["amqp://localhost:5672"], queue: "orders_queue" },
});

Transport sozlashtransport + options. Redis (host/port), RabbitMQ (urls/queue), Kafka (brokers). Faqat shu o'zgaradi — handler kodi bir xil 2.5-bob. Bu — NestJS'ning kuchi (transport-agnostik). Production'da Redis/RabbitMQ/Kafka (TCP emas).

Barcha transport'ning options maydonlari — bir joyda (har biri o'rnatilishi kerak bo'lgan paket bilan):

ts
// 1) TCP — tug'ma (qo'shimcha paket yo'q). Eng oddiy — o'rganish uchun.
{ transport: Transport.TCP,   options: { host: "0.0.0.0", port: 3001, retryAttempts: 5, retryDelay: 3000 } }

// 2) Redis — `npm i ioredis`. Yengil pub/sub 5.21-bob.
{ transport: Transport.REDIS, options: { host: "localhost", port: 6379, retryAttempts: 5, retryDelay: 3000 } }

// 3) NATS — `npm i nats`. Yengil, o'ta tez pub/sub (2.4a).
{ transport: Transport.NATS,  options: { servers: ["nats://localhost:4222"], queue: "orders" } }

// 4) RabbitMQ — `npm i amqplib amqp-connection-manager`. Navbat, routing, dead-letter (2.4b).
{ transport: Transport.RMQ,   options: { urls: ["amqp://localhost:5672"], queue: "orders_queue",
                                         queueOptions: { durable: true }, noAck: false } }

// 5) Kafka — `npm i kafkajs`. Event streaming, durable, partition, replay (2.4c).
{ transport: Transport.KAFKA, options: { client: { brokers: ["localhost:9092"] },
                                         consumer: { groupId: "orders-consumer" } } }

// 6) gRPC — `npm i @grpc/grpc-js @grpc/proto-loader`. Tez, tipli (protobuf — 2.4d).
{ transport: Transport.GRPC,  options: { package: "orders", protoPath: join(__dirname, "orders.proto"),
                                         url: "localhost:5000" } }

Muhim: TCP tug'ma (paketsiz), boshqalari — mos drayver paketi kerak (ioredis, nats, amqplib, kafkajs, @grpc/grpc-js). retryAttempts/retryDelay — klient ulanish uzilsa qayta urinish (TCP/Redis/NATS). RabbitMQ/Kafka'da ishonchlilik broker darajasida (navbat/offset).

2.4a. NATS transport (yengil, tez pub/sub)

ts
// NATS mikroservis — yengil, past kechikish (low latency)
import { Transport } from "@nestjs/microservices";

const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.NATS,
  options: {
    servers: ["nats://localhost:4222"],
    queue: "orders_group",          // queue group — bir guruhda faqat bitta instance oladi (load balance)
  },
});

// Klient
ClientsModule.register([
  { name: "NATS_SERVICE", transport: Transport.NATS,
    options: { servers: ["nats://localhost:4222"] } },
]);

NATS — o'ta yengil, tez message tizimi (Go'da yozilgan broker). Queue group (queue) — bir guruhda ko'p instance bo'lsa, har xabarni faqat bittasi oladi (yuk balansi — masshtab). Request-response (send) ham, pub/sub (emit) ham. Redis'ga o'xshash, lekin maxsus message uchun optimallashgan. Kichik-o'rta tizim uchun ideal (RabbitMQ'dan yengilroq).

2.4b. RabbitMQ transport (navbat — durable, ack)

ts
// RabbitMQ — vazifa navbati (task queue), ishonchli yetkazish
const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.RMQ,
  options: {
    urls: ["amqp://user:pass@localhost:5672"],
    queue: "tasks_queue",
    queueOptions: { durable: true },   // navbat restart'da saqlanadi (diskda)
    noAck: false,                      // qo'lda tasdiq (ack) — ishonchli (yo'qolmaydi)
    prefetchCount: 1,                  // bir vaqtda 1 vazifa (adolatli taqsimot)
  },
});

RabbitMQ — navbat asosli (message broker). durable: true — navbat diskda (broker restart'da yo'qolmaydi). noAck: false — handler tugagach channel.ack chaqiriladi (aks holda xabar qayta yuboriladi — ishonchli, at-least-once). prefetchCount — bir instance bir vaqtda nechta olishi (yuk balansi). Routing, dead-letter, retry — RabbitMQ kuchi (vazifa qayta ishlash — Misol 5).

2.4c. Kafka transport (event streaming — durable, replay)

ts
// Kafka — event streaming, durable log, partition, replay
const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.KAFKA,
  options: {
    client: {
      clientId: "orders-service",
      brokers: ["localhost:9092"],
    },
    consumer: {
      groupId: "orders-consumer",     // consumer group — partition'lar guruhga taqsimlanadi
    },
    run: { autoCommit: true },
  },
});

// Kafka'da request-response — javob alohida topic'ga boradi (reply topic).
// Handler avtomatik `<pattern>.reply` topic'iga javob yozadi (NestJS boshqaradi).
@MessagePattern("order.get")
bitta(@Payload() message: any) {
  return this.service.bitta(message.value.id);   // message.value — Kafka payload
}

Kafka — event streaming platformasi (durable, taqsimlangan log). Consumer group — partition'lar guruh a'zolariga taqsimlanadi (masshtab). Xabar saqlanadi (log — replay mumkin: eski voqealarni qayta o'qish, event sourcing). message.value — Kafka'da payload konvert ichida (offset, partition, headers bilan). Og'ir infratuzilma — juda katta hajm, event sourcing, audit log uchun. Request-response NestJS'da reply-topic orqali (avtomatik).

2.4d. gRPC transport (tez, tipli — protobuf)

gRPC — Google'ning yuqori unumli RPC protokoli. Xizmat va message'lar .proto faylida (contract-first) e'lon qilinadi, so'ng binar (protobuf) formatda uzatiladi — JSON'dan tez va tipli. Xizmatlararo ichki RPC uchun eng samarali.

proto
// orders.proto — kontrakt (xizmat + message tuzilishi)
syntax = "proto3";

package orders;

service OrdersService {
  rpc FindOne (OrderById) returns (Order) {}          // unary (so'rov-javob)
  rpc FindMany (Empty) returns (stream Order) {}      // server streaming (oqim)
}

message OrderById { int32 id = 1; }
message Empty {}
message Order {
  int32 id = 1;
  string title = 2;
  double price = 3;
}
ts
// main.ts — gRPC mikroservis
import { join } from "path";

const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
  transport: Transport.GRPC,
  options: {
    package: "orders",                              // .proto'dagi package nomi
    protoPath: join(__dirname, "orders.proto"),     // .proto fayl yo'li
    url: "localhost:5000",                          // manzil
  },
});
await app.listen();

// orders.controller.ts — @GrpcMethod (handler)
import { GrpcMethod } from "@nestjs/microservices";
import { Observable, Subject } from "rxjs";

@Controller()
export class OrdersController {
  @GrpcMethod("OrdersService", "FindOne")           // (service nomi, rpc metodi)
  findOne(data: { id: number }): Order {
    return this.service.bitta(data.id);             // .proto'dagi Order qaytariladi
  }

  @GrpcMethod("OrdersService", "FindMany")          // server streaming
  findMany(): Observable<Order> {
    const subject = new Subject<Order>();
    this.service.hammasi().forEach((o) => subject.next(o));
    subject.complete();
    return subject.asObservable();                  // oqim (stream) qaytariladi
  }
}
ts
// Klient tomon — gRPC ClientProxy (proto'dan interfeys)
import { ClientGrpc, Transport } from "@nestjs/microservices";
import { OnModuleInit } from "@nestjs/common";

@Module({
  imports: [
    ClientsModule.register([
      { name: "ORDERS_PACKAGE", transport: Transport.GRPC,
        options: { package: "orders", protoPath: join(__dirname, "orders.proto"), url: "localhost:5000" } },
    ]),
  ],
})
export class GatewayModule {}

@Injectable()
export class GatewayService implements OnModuleInit {
  private ordersService: OrdersGrpcInterface;
  constructor(@Inject("ORDERS_PACKAGE") private client: ClientGrpc) {}

  onModuleInit() {
    // proto'dagi service nomidan tipli klient olinadi
    this.ordersService = this.client.getService<OrdersGrpcInterface>("OrdersService");
  }

  findOne(id: number): Observable<Order> {
    return this.ordersService.findOne({ id });      // metod chaqiruvi (Observable qaytaradi)
  }
}

gRPC — protobuf (.proto) bilan contract-first, tipli, binar (JSON'dan tez). @GrpcMethod("Service", "Method") — handler (route emas — proto metodi). Klientda ClientGrpc + getService() — proto'dan tipli interfeys (metod chaqiruvi). Streaming — unary (bitta javob) va server/client/bidirectional stream (stream kalit so'zi proto'da — Observable). Xizmatlararo tez, tipli RPC uchun eng yaxshi (ayniqsa ichki, polyglot tizim — proto til-agnostik).

2.4e. Serialization va Deserialization (xabar formatlash)

ts
// Standart: NestJS avtomatik JSON serialize/deserialize qiladi.
// Maxsus format kerak bo'lsa — Serializer / Deserializer.
import { Serializer, OutgoingResponse } from "@nestjs/microservices";

export class CustomSerializer implements Serializer {
  serialize(value: any): any {
    // yuborishdan oldin (masalan konvert, sana  ISO, maxsus format)
    return { data: value, sentAt: new Date().toISOString() };
  }
}

// Transport options'da ulash:
{
  transport: Transport.TCP,
  options: {
    port: 3001,
    serializer: new CustomSerializer(),
    // deserializer: new CustomDeserializer(),   // qabulda teskari
  },
}

Serialization — obyekt tarmoq orqali uzatiladigan formatga aylantirilishi (default: JSON). NestJS buni avtomatik qiladi — oddiy holatda hech narsa yozmaysiz. Lekin boshqa tizim bilan mos format (masalan, konvertli xabar, custom protokol) kerak bo'lsa — Serializer (yuborishda) va Deserializer (qabulda) interfeysini implement qilib, transport options'iga berasiz. gRPC/Kafka'da o'z formati bor (protobuf/Kafka message), shuning uchun bu asosan TCP/Redis/RMQ uchun.

2.4f. Xato, timeout va retry (ishonchlilik)

ts
import { firstValueFrom, timeout, retry, catchError, throwError } from "rxjs";

async buyurtmaOl(id: number) {
  return firstValueFrom(
    this.client.send({ cmd: "order_get" }, id).pipe(
      timeout(5000),                                  // 5s ichida javob kelmasa — xato
      retry({ count: 3, delay: 1000 }),               // 3 marta qayta urinish (1s oraliq)
      catchError((err) =>                             // barchasi muvaffaqiyatsiz bo'lsa
        throwError(() => new ServiceUnavailableException("Buyurtma xizmati javob bermadi")),
      ),
    ),
  );
}

Ishonchlilik — tarmoq — ishonchsiz (mikroservis realligi). send — RxJS Observable, shuning uchun operatorlar bilan boyitiladi: timeout(ms) (javob kutish chegarasi — osilib qolmaslik), retry({ count, delay }) (vaqtinchalik uzilishda qayta urinish), catchError (mahalliy fallback yoki HTTP exception'ga aylantirish). Transport options'idagi retryAttempts/retryDelay — bu ulanish darajasidagi retry (Observable'dagi — so'rov darajasidagi). Idempotent handler bilan retry xavfsiz (6-xato).

2.5. Message Pattern (so'rov-javob — request-response)

ts
// Mikroservis (qabul qiluvchi) — handler
import { MessagePattern, Payload } from "@nestjs/microservices";

@Controller()
export class OrdersController {
  @MessagePattern({ cmd: "buyurtma_yarat" })          // pattern (route kabi)
  yarat(@Payload() data: CreateOrderDto) {            // ma'lumot
    return this.ordersService.yarat(data);            // JAVOB qaytaradi
  }
  @MessagePattern({ cmd: "buyurtma_ol" })
  bitta(@Payload() id: number) {
    return this.ordersService.bitta(id);
  }
}

@MessagePatternso'rov-javob (request-response): klient xabar yuboradi, javob kutadi (HTTP'ga o'xshash — controller route). { cmd: "..." } — pattern (mos handler). @Payload() — ma'lumot. Javob qaytaradi. Klient 2.7-bob bloklanadi (javobgacha kutadi). Sinxron aloqa uchun (ma'lumot kerak).

2.6. Event Pattern (fire-and-forget — voqea)

ts
@EventPattern("buyurtma_yaratildi")                  // voqea (event)
async buyurtmaYaratildi(@Payload() data: any) {
  await this.emailService.yubor(data.email);          // email yuborish
  // JAVOB QAYTARMAYDI (fire-and-forget)
}

@EventPatternvoqea (fire-and-forget): klient voqea e'lon qiladi va kutmaydi (javobsiz — davom etadi). Bir voqeani ko'p xizmat tinglashi mumkin (buyurtma yaratildi email + SMS + statistika xizmatlari). Asinxron, bo'shashgan bog'lanish (loose coupling — 9). Voqea-asoslangan arxitektura asosi (event-driven).

2.7. ClientProxy (xizmatlararo aloqa)

ts
// Klient (yuboruvchi xizmat) — ClientProxy
import { ClientProxy, ClientsModule, Transport } from "@nestjs/microservices";

@Module({
  imports: [
    ClientsModule.register([
      { name: "ORDERS_SERVICE", transport: Transport.TCP, options: { port: 3001 } },
    ]),
  ],
})
export class GatewayModule {}

@Injectable()
export class GatewayService {
  constructor(@Inject("ORDERS_SERVICE") private client: ClientProxy) {}

  buyurtmaYarat(dto: any) {
    return this.client.send({ cmd: "buyurtma_yarat" }, dto);   // so'rov-javob (Observable)
  }
  voqeaElon(data: any) {
    this.client.emit("buyurtma_yaratildi", data);              // voqea (fire-and-forget)
  }
}

ClientProxy — boshqa xizmatga xabar yuborish. client.send(pattern, data) — so'rov-javob (Observable — javob kutadi — 2.5); client.emit(event, data) — voqea (kutmaydi — 2.6). ClientsModule.register (klient sozlash). Bu — xizmatlar orasidagi aloqa (gateway service). RxJS Observable 8.6-bob.

send — "cold" Observable: send() obyekt qaytaradi, lekin subscribe qilinmaguncha (yoki firstValueFrom bilan) xabar aslida yuborilmaydi (Misol 8, 3-xato). Har subscribe — yangi so'rov (natijani qayta ishlatmoqchi bo'lsangiz — firstValueFrom bilan bir marta oling). emit esa fire-and-forget — subscribe kutmaydi, lekin ishonchli yetkazish uchun ba'zi transportlar (RMQ/Kafka) emit natijasini ham await qilishni qo'llab-quvvatlaydi (ulanish tayyorligini kutish). ClientProxy'ni yopishonApplicationShutdown'da client.close() (ulanishni tozalash — 8.15).

2.8. API Gateway (yagona kirish nuqtasi)

text
  API GATEWAY naqsh:
          ┌─────────────┐
  Mijoz  │ API Gateway │ (HTTP — yagona kirish)
          └──────┬──────┘
        ┌────────┼────────┐
        ▼        ▼        ▼
    Users    Orders   Products   (mikroservislar — TCP/Redis)
    Service  Service  Service
    (DB)     (DB)     (DB)

  Gateway: auth, rate limit, routing, javoblarni birlashtirish

API Gateway — mijoz uchun yagona kirish (HTTP). Mijoz har xizmatga to'g'ridan murojaat qilmaydi — Gateway'ga (u mikroservislarga yo'naltiradi — ClientProxy). Gateway: auth 8.9-bob, rate limit 2.13-bob, routing, javoblarni birlashtirish. Bu — mikroservis arxitekturasining standart naqshi (mijoz murakkablikni ko'rmaydi).

2.9. Hybrid application (HTTP + mikroservis)

ts
// Bir ilova: HTTP + mikroservis (gateway)
async function bootstrap() {
  const app = await NestFactory.create(AppModule);     // HTTP
  app.connectMicroservice({                            // + mikroservis transport
    transport: Transport.REDIS,
    options: { host: "localhost", port: 6379 },
  });
  await app.startAllMicroservices();                   // mikroservislarni boshlash
  await app.listen(3000);                              // HTTP ham
}
ts
// Bir nechta transportni ulash mumkin (ko'p connectMicroservice)
const app = await NestFactory.create(AppModule);
app.connectMicroservice({ transport: Transport.REDIS, options: { host: "localhost", port: 6379 } });
app.connectMicroservice({ transport: Transport.RMQ,   options: { urls: ["amqp://localhost:5672"], queue: "tasks" } });
// { inheritAppConfig: true } — global pipe/filter/interceptor'ni mikroservisga ham qo'llaydi
await app.startAllMicroservices();   // BARCHA ulangan transportlarni birga ishga tushiradi
await app.listen(3000);              // HTTP ham tinglaydi

Hybrid app — bir NestJS ilova HTTP + mikroservis birga. connectMicroservice (transport qo'shish — bir nechta chaqirilishi mumkin: Redis + RMQ birga) + startAllMicroservices (barchasini ishga tushirish). { inheritAppConfig: true } — HTTP uchun sozlangan global pipe/guard/filter'ni mikroservis handlerlarga ham qo'llaydi (8.5, 8.6). Gateway shunday (tashqi HTTP + ichki message). Bir DI konteyner, modul daraxti. Gateway uchun ideal (public API + internal RPC).

2.10. Taqsimlangan tranzaksiya (Saga — qiyin muammo)

text
  Muammo: mikroservislarda umumiy DB yo'q (har xizmat o'z DB'si)
   bir tranzaksiya bir necha xizmatni qamraydi (buyurtma + to'lov + zaxira)
   oddiy DB transaction 8.13-bob ishlamaydi (taqsimlangan!)

  Yechim — SAGA naqshi:
  - Har qadam alohida (buyurtma  to'lov  zaxira)
  - Xato bo'lsa — KOMPENSATSIYA (orqaga: to'lov qaytarish, buyurtma bekor)
  - Choreography (event) yoki Orchestration (markaziy)

Taqsimlangan tranzaksiya (mikroservisning eng qiyin muammosi): har xizmat o'z DB'si oddiy ACID transaction 8.13-bob ishlamaydi. Saga naqshi: ketma-ket qadamlar + xato bo'lsa kompensatsiya (orqaga qaytarish). Choreography (event — 2.6) yoki orchestration (markaziy boshqaruvchi). Bu — mikroservisning murakkabligi (monolitda yo'q — 2.1). 9-QISM'da chuqur.

2.11. Exception va validatsiya (mikroservis)

ts
import { RpcException } from "@nestjs/microservices";

@MessagePattern({ cmd: "buyurtma_ol" })
async bitta(@Payload() id: number) {
  const order = await this.service.bitta(id);
  if (!order) throw new RpcException("Buyurtma topilmadi");   // RPC xato (8.6 filter)
  return order;
}
// Gateway'da RpcException  HTTP exception'ga aylantiriladi
ts
// RPC exception filter — mikroservis tomonda (xatoni formatlab qaytarish)
import { Catch, RpcExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { RpcException } from "@nestjs/microservices";
import { Observable, throwError } from "rxjs";

@Catch(RpcException)
export class RpcErrorFilter implements RpcExceptionFilter<RpcException> {
  catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
    return throwError(() => exception.getError());   // klientga xato Observable orqali qaytadi
  }
}

// Klient (Gateway) tomonda RPC xatoni HTTP'ga aylantirish:
async bitta(id: number) {
  return firstValueFrom(
    this.client.send({ cmd: "buyurtma_ol" }, id).pipe(
      catchError((err) => {                          // RpcException'dan kelgan xato
        throw new NotFoundException(err.message ?? err);   //  HTTP 404
      }),
    ),
  );
}

Mikroservis exceptionRpcException (HTTP exception emas — 8.1). Mikroservis tomonda xato Observable orqali klientga qaytadi (HTTP status yo'q — transport RPC). Gateway'da RPC xato HTTP'ga aylantiriladi (catchError + HTTP exception, yoki RpcExceptionFilter — 8.6). Validatsiya (ValidationPipe — 8.5) ham ishlaydi (message payload validatsiya qilinadi — noto'g'ri payload RpcException). Pipe/guard/interceptor/filter — mikroservisda ham ishlaydi (8.5, 8.6). Bir xil NestJS imkoniyatlar, faqat kontekst RPC (host.switchToRpc()).

2.12. Service discovery va aloqa (kontekst)

text
  Mikroservislar bir-birini qanday topadi?
  - Statik (config — kichik tizim — port/host)
  - Service registry (Consul, Eureka — dinamik)
  - Kubernetes service (10 — DNS, load balancing)
  - Message broker (RabbitMQ/Kafka — broker orqali — discovery kerak emas)

   Broker (RabbitMQ/Kafka) — eng oson (xizmatlar brokerga ulanadi, bir-birini bilmaydi)

Service discovery — xizmatlar bir-birini topishi. Statik (config — 8.14), service registry (Consul), Kubernetes (10 — DNS), yoki message broker (RabbitMQ/Kafka — xizmatlar brokerga ulanadi, bir-birini to'g'ridan bilmaydi — eng oson). Broker yondashuvi — bo'shashgan bog'lanish (9). DevOps (10) bilan bog'liq.

2.13. Rate limiting va Throttler (Gateway — 14)

ts
import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler";

@Module({
  imports: [
    ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }]),   // 1 daqiqada 100
  ],
  providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],   // global (8.6)
})
export class GatewayModule {}

// Yoki endpoint
@Throttle({ default: { limit: 5, ttl: 60000 } })     // login (brute-force — 8.9)
@Post("login")
login() {}

Throttler (rate limiting — 14, 5.20) — Gateway'da so'rov sonini cheklash (DoS, brute-force himoyasi). ThrottlerModule + ThrottlerGuard (global — 8.6). @Throttle (endpoint — login — 8.9). Redis bilan (taqsimlangan — ko'p instance — 8.15). Gateway — auth + rate limit + routing 2.8-bob.

2.14. Best practices (mikroservis)

text
   Monolit'dan boshlang (kerak bo'lganda ajrating — "monolith first" — 2.1)
   To'g'ri transport (Redis/RabbitMQ/Kafka — ehtiyojga — 2.3)
   Message (so'rov-javob) vs Event (fire-and-forget — to'g'ri tanlov — 2.5, 2.6)
   API Gateway (yagona kirish — auth/rate limit — 2.8)
   Event-driven (bo'shashgan bog'lanish — 2.6, 9)
   Saga (taqsimlangan tranzaksiya — 2.10); idempotent handler
   Har xizmat o'z DB'si (mustaqillik); RpcException 2.11-bob
   Monitoring/tracing (taqsimlangan — NestJS 11 trace ID — 2.12, 10)

3. Sintaksis — tez ma'lumotnoma

ts
// Mikroservis 2.2-bob: createMicroservice(AppModule, { transport, options })
// Handler (2.5, 2.6)
@MessagePattern({ cmd: "x" })  yarat(@Payload() data) {}   // so'rov-javob
@EventPattern("voqea")  handle(@Payload() data) {}         // voqea

// Klient (2.7)
ClientsModule.register([{ name: "SVC", transport, options }])
@Inject("SVC") client: ClientProxy
client.send(pattern, data)   // so'rov-javob (Observable)
client.emit(event, data)     // voqea

// gRPC (2.4d)
@GrpcMethod("OrdersService", "FindOne")  findOne(data) {}   // proto metodi
this.client.getService<T>("OrdersService")                 // klient tipli interfeys

// Hybrid 2.9-bob: app.connectMicroservice(); app.startAllMicroservices()

// Ishonchlilik (2.4f): .pipe(timeout(5000), retry({ count: 3, delay: 1000 }), catchError(...))
// Serializer (2.4e): options.serializer / options.deserializer (maxsus format)

4. Batafsil kod namunalari

Misol 1 — Orders mikroservis (2.2, 2.5)

ts
// orders/main.ts
async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(OrdersModule, {
    transport: Transport.REDIS,
    options: { host: "localhost", port: 6379 },
  });
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));   // (8.5)
  await app.listen();
  console.log("Orders mikroservis ishlayapti");
}
bootstrap();

// orders/orders.controller.ts
@Controller()
export class OrdersController {
  constructor(private ordersService: OrdersService) {}

  @MessagePattern({ cmd: "order_create" })            // so'rov-javob
  yarat(@Payload() dto: CreateOrderDto) {
    return this.ordersService.yarat(dto);
  }

  @MessagePattern({ cmd: "order_get" })
  bitta(@Payload() id: number) {
    return this.ordersService.bitta(id);
  }

  @EventPattern("payment_completed")                  // voqea (to'lov xizmatidan)
  async tolovTugadi(@Payload() data: { orderId: number }) {
    await this.ordersService.holatYangila(data.orderId, "to'langan");
  }
}

Misol 2 — API Gateway (2.7, 2.8, 2.9)

ts
// gateway/gateway.module.ts
@Module({
  imports: [
    ClientsModule.register([
      { name: "ORDERS_SERVICE", transport: Transport.REDIS, options: { host: "localhost", port: 6379 } },
      { name: "USERS_SERVICE", transport: Transport.REDIS, options: { host: "localhost", port: 6379 } },
    ]),
  ],
  controllers: [GatewayController],
})
export class GatewayModule {}

// gateway/gateway.controller.ts (HTTP — mijoz uchun)
@Controller("orders")
export class GatewayController {
  constructor(
    @Inject("ORDERS_SERVICE") private ordersClient: ClientProxy,
    @Inject("USERS_SERVICE") private usersClient: ClientProxy,
  ) {}

  @Post()
  @UseGuards(JwtAuthGuard)                            // auth 8.9-bob — Gateway'da
  yarat(@Body() dto: CreateOrderDto) {
    return this.ordersClient.send({ cmd: "order_create" }, dto);   // mikroservisga
  }

  @Get(":id")
  async bitta(@Param("id") id: number) {
    // Bir necha xizmatdan birlashtirish (2.8)
    const order = await firstValueFrom(this.ordersClient.send({ cmd: "order_get" }, id));
    const user = await firstValueFrom(this.usersClient.send({ cmd: "user_get" }, order.userId));
    return { ...order, user };                         // birlashtirilgan javob
  }
}

Misol 3 — Event-driven (voqea — 2.6)

ts
// Buyurtma yaratilganda — ko'p xizmat tinglaydi (loose coupling)
// Gateway/Orders — voqea e'lon
@Injectable()
export class OrdersService {
  constructor(@Inject("EVENT_BUS") private eventBus: ClientProxy) {}

  async yarat(dto: CreateOrderDto) {
    const order = await this.repo.save(dto);
    this.eventBus.emit("order_created", {              // voqea (fire-and-forget — 2.6)
      orderId: order.id, userId: order.userId, email: order.email,
    });
    return order;                                       // kutmaydi
  }
}

// Email mikroservis — tinglaydi
@EventPattern("order_created")
async emailYubor(@Payload() data: any) {
  await this.mailService.buyurtmaTasdiq(data.email, data.orderId);
}

// SMS mikroservis — ham tinglaydi (mustaqil)
@EventPattern("order_created")
async smsYubor(@Payload() data: any) {
  await this.smsService.yubor(data.userId);
}
//  bir voqea, ko'p tinglovchi (Orders ularni bilmaydi — bo'shashgan)

Misol 4 — Saga (taqsimlangan tranzaksiya — 2.10)

ts
// Orchestration saga (markaziy boshqaruvchi)
@Injectable()
export class OrderSaga {
  constructor(
    @Inject("PAYMENT") private payment: ClientProxy,
    @Inject("INVENTORY") private inventory: ClientProxy,
  ) {}

  async buyurtmaYarat(dto: CreateOrderDto) {
    // 1. Zaxira band qilish
    const zaxira = await firstValueFrom(this.inventory.send({ cmd: "reserve" }, dto.items));
    try {
      // 2. To'lov
      const tolov = await firstValueFrom(this.payment.send({ cmd: "charge" }, dto));
      return { success: true, tolov };
    } catch (e) {
      // KOMPENSATSIYA — zaxirani qaytarish (2.10)
      await firstValueFrom(this.inventory.send({ cmd: "release" }, zaxira.id));
      throw new BadRequestException("To'lov amalga oshmadi, buyurtma bekor qilindi");
    }
  }
}

Misol 5 — RabbitMQ (navbat — vazifa qayta ishlash)

ts
// RabbitMQ mikroservis (vazifa navbati)
const app = await NestFactory.createMicroservice(AppModule, {
  transport: Transport.RMQ,
  options: {
    urls: ["amqp://localhost:5672"],
    queue: "tasks_queue",
    queueOptions: { durable: true },                  // navbat saqlanadi (restart'da)
    noAck: false,                                      // qo'lda tasdiq (ishonchli)
  },
});

@EventPattern("process_video")
async videoQaytaIshla(@Payload() data: any, @Ctx() context: RmqContext) {
  const channel = context.getChannelRef();
  const message = context.getMessage();
  try {
    await this.videoService.qaytaIshla(data);
    channel.ack(message);                             // muvaffaqiyat  tasdiq
  } catch (e) {
    channel.nack(message);                            // xato  qayta (yoki dead-letter)
  }
}

Misol 6 — Throttler (Gateway rate limit — 2.13)

ts
// gateway.module.ts
@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      inject: [ConfigService],
      useFactory: () => ({
        throttlers: [{ ttl: 60000, limit: 100 }],     // umumiy: 1 daqiqada 100
        // storage: Redis (taqsimlangan — 8.15)
      }),
    }),
  ],
  providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
})
export class GatewayModule {}

// Maxsus limit (login — brute-force — 8.9)
@Throttle({ default: { limit: 5, ttl: 60000 } })
@Post("auth/login")
login(@Body() dto: LoginDto) {}

Misol 7 — Mikroservis tuzilishi (monorepo)

text
  Mikroservis loyiha (monorepo — NestJS workspace):
  apps/
  ├── gateway/        (HTTP — API Gateway, auth, routing — Misol 2)
  ├── users/          (foydalanuvchi xizmati — o'z DB)
  ├── orders/         (buyurtma xizmati — o'z DB — Misol 1)
  ├── payment/        (to'lov xizmati)
  └── notification/   (email/SMS — event tinglovchi — Misol 3)
  libs/
  └── common/         (umumiy DTO, interface, konstanta)

   Har xizmat mustaqil (o'z DB, deploy); libs — umumiy kod
   nest generate app <nom> (monorepo)

Misol 8 — Async send va xato boshqaruv (2.11)

ts
import { firstValueFrom, timeout, catchError } from "rxjs";

@Injectable()
export class GatewayService {
  constructor(@Inject("ORDERS") private client: ClientProxy) {}

  async buyurtmaOl(id: number) {
    try {
      return await firstValueFrom(
        this.client.send({ cmd: "order_get" }, id).pipe(
          timeout(5000),                              // 5 soniya kutish (timeout)
          catchError((e) => { throw new ServiceUnavailableException("Xizmat javob bermadi"); }),
        ),
      );
    } catch (e) {
      throw e;
    }
  }
}

Misol 9 — Health check (mikroservis — 2.12, 8.15)

ts
// Har mikroservis — health (monitoring — 10)
@Controller()
export class HealthController {
  @MessagePattern({ cmd: "health" })
  health() {
    return { status: "ok", service: "orders", time: new Date().toISOString() };
  }
}
// Gateway — barcha xizmat health (aggregation)
@Get("health")
async health() {
  const services = ["ORDERS", "USERS", "PAYMENT"];
  // har biriga health so'rovi  birlashtirilgan holat
}

Misol 10 — Monolit mikroservis o'tish (2.1)

text
  O'tish strategiyasi (monolith first — 2.1):
  1. Monolit (modulli — 8.1) — toza modul chegaralari
  2. Og'ir/mustaqil modulni aniqlash (masalan: notification, video)
  3. Shu modulni alohida xizmat qilish (strangler fig naqsh)
  4. Event/message bilan ulash 2.6-bob
  5. Asta-sekin boshqalarni (kerak bo'lsa)

   Hammasi birdan emas — bosqichma-bosqich (xavf kam)
   Toza modul 8.1-bob — keyin oson ajratiladi

5. To'g'ri va noto'g'ri holatlar

1) Erta mikroservis (kichik loyiha)

text
 kichik loyiha  mikroservis (keraksiz murakkablik — 2.1)
 monolit (modulli)  kerak bo'lganda ajrating

2) So'rov-javob hammasiga (event kerak joyda)

text
 email'ni send (kutadi — sekin)
 emit (voqea — fire-and-forget — 2.6)

3) Mikroservisda umumiy DB

text
 bir DB (bog'lanish — mustaqillik yo'q)
 har xizmat o'z DB'si (2.1)

4) Taqsimlangan tranzaksiya oddiy transaction bilan

text
 DB transaction (taqsimlanmaydi — 2.10)
 Saga (kompensatsiya)

5) Mijoz to'g'ridan xizmatlarga

text
 mijoz har xizmatga (murakkab, xavfsizlik)
 API Gateway (yagona kirish — 2.8)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Mikroservis ulanmaydi

Sababi: transport/port mos emas, broker ishlamayapti 2.4-bob. Yechimi: transport bir xil; Redis/RabbitMQ ishga tushir.

Xato 2 — send javob bermaydi (osilib qoladi)

Sababi: pattern mos emas, yoki handler yo'q 2.5-bob. Yechimi: pattern aniq mos; timeout qo'shing (Misol 8).

Xato 3 — Observable subscribe bo'lmaydi

Sababi: send Observable (RxJS) — await emas. Yechimi: firstValueFrom (Misol 8).

Xato 4 — Event yo'qoladi

Sababi: durable emas, yoki tinglovchi yo'q edi 2.6-bob. Yechimi: durable queue (RabbitMQ); Kafka (replay).

Xato 5 — Taqsimlangan ma'lumot nomutanosib

Sababi: Saga/kompensatsiya yo'q 2.10-bob. Yechimi: Saga; idempotent; event sourcing.

Xato 6 — Bir voqea ikki marta

Sababi: at-least-once delivery. Yechimi: idempotent handler (ikki marta = bir marta).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Arxitektura (9): monolit vs mikroservis, Saga, event-driven.
  • DevOps (10): Docker, Kubernetes, service discovery, deploy.
  • Redis 8.15-bob: transport, throttler.
  • Auth 8.9-bob: Gateway auth.
  • Event 8.6-bob: EventPattern, RxJS.
  • DB 8.13-bob: har xizmat o'z DB.
  • Throttler 5.20-bob: rate limit.
  • Navbat 8.22-bob: RabbitMQ/BullMQ.
  • Monitoring 8.15-bob: health, tracing.

8. Eng yaxshi amaliyotlar (best practices)

  • Monolit'dan boshlang ("monolith first" — kerak bo'lganda ajrating — 2.1).
  • To'g'ri transport (Redis/RabbitMQ/Kafka — ehtiyojga — 2.3).
  • Message vs Event (so'rov-javob vs fire-and-forget — 2.5, 2.6).
  • API Gateway (yagona kirish — auth/rate limit — 2.8).
  • Event-driven (bo'shashgan bog'lanish — 2.6, 9).
  • Saga (taqsimlangan tranzaksiya — 2.10); idempotent handler.
  • Har xizmat o'z DB'si (mustaqillik — 2.1).
  • RpcException (mikroservis xato — 2.11); timeout (Misol 8).
  • Throttler (rate limit — Gateway — 2.13).
  • Monitoring/tracing (taqsimlangan — 2.12, 10).

9. Amaliy loyiha: "Mikroservis E-commerce Tizimi"

NestJS mikroservislarni mustahkamlash.

Maqsad

E-commerce'ni mikroservislarga ajratish: Gateway + Users + Orders + Payment + Notification — message/event bilan.

Talablar (requirements)

  1. Mikroservislar: Orders, Users (Redis transport — Misol 1, 2.2).
  2. Message pattern: CRUD (so'rov-javob — Misol 1, 2.5).
  3. Event pattern: order_created email/SMS (Misol 3, 2.6).
  4. API Gateway: HTTP + ClientProxy + auth (Misol 2, 2.8).
  5. Hybrid: Gateway HTTP + mikroservis 2.9-bob.
  6. Saga: buyurtma + to'lov + zaxira + kompensatsiya (Misol 4, 2.10).
  7. RabbitMQ: vazifa navbati (video/email — Misol 5).
  8. Throttler: Gateway rate limit (Misol 6, 2.13).
  9. Xato + timeout: RpcException, firstValueFrom + timeout (Misol 8, 2.11).
  10. Health: har xizmat (Misol 9, 2.12).

Maslahatlar (hint)

  • Transport bir xil (2.4, 1-xato).
  • send firstValueFrom (2.7, 3-xato).
  • Email emit (event — 2.6, 2-holat).
  • Saga kompensatsiya (2.10, 4-holat).
  • Gateway yagona kirish (2.8, 5-holat).
  • Idempotent handler (6-xato).

"Tayyor" mezonlari (acceptance criteria)

  • Mikroservislar (transport).
  • Message pattern (CRUD).
  • Event pattern (ko'p tinglovchi).
  • API Gateway (ClientProxy, auth).
  • Hybrid app.
  • Saga (kompensatsiya).
  • RabbitMQ navbat.
  • Throttler.
  • Xato + timeout.
  • Health check.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda NestJS mikroservislarni chuqur o'rgandik:

  • Monolit vs mikroservis (qachon qaysi — "monolith first" — 2.1); NestJS mikroservis 2.2-bob; transport'lar (TCP/Redis/NATS/RabbitMQ/Kafka/gRPC — 2.3, 2.4; har biri config — 2.4a–2.4d).
  • Message pattern (so'rov-javob — 2.5); Event pattern (fire-and-forget — 2.6); ClientProxy (xizmatlararo, send cold Observable — 2.7); serialization (2.4e); timeout/retry (2.4f).
  • gRPC (proto + @GrpcMethod + streaming — 2.4d); NATS (yengil pub/sub — 2.4a); Kafka (event streaming, replay — 2.4c).
  • API Gateway (yagona kirish — 2.8); hybrid app 2.9-bob; Saga (taqsimlangan tranzaksiya — 2.10); RpcException 2.11-bob.
  • Service discovery 2.12-bob; Throttler (rate limit — 2.13); best practices 2.14-bob.

Keyingi bob — 8.17-bob: GraphQL (NestJS GraphQL). Mikroservisni bildik; endi REST'ga muqobil API uslubini — GraphQL — o'rganamiz: code-first vs schema-first, resolver (@Query/@Mutation), @ObjectType, subscriptions (real-time), va N+1 muammosi (DataLoader). GraphQL — mijoz aniq kerakli ma'lumotni so'raydigan moslashuvchan API.


Foydalanilgan rasmiy/ishonchli manbalar

  • docs.nestjs.com/microservices/basics (transport, @MessagePattern, @EventPattern, ClientProxy)
  • docs.nestjs.com/microservices/redis · docs.nestjs.com/microservices/rabbitmq · docs.nestjs.com/microservices/kafka
  • docs.nestjs.com/microservices/nats · docs.nestjs.com/microservices/grpc (proto, @GrpcMethod)
  • docs.nestjs.com/microservices/exception-filters · docs.nestjs.com/faq/hybrid-application

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.16-bob: Microservices — NestJS mikroservislar — Wisar