NestJS एंटरप्राइज़ बैकएंड API · पाठ

कमांड, हैंडलर और CommandBus

लेखन कार्यों को कमांड के रूप में मॉडल कीजिए और उन्हें CommandBus के माध्यम से समर्पित हैंडलरों तक भेजिए।

पाठ 1, कुल 4 में से13 चरण

कमांड, हैंडलर और CommandBus, CoddyKit पर NestJS एंटरप्राइज़ बैकएंड API का एक निःशुल्क पाठ है। यह 4 में से 1वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह NestJS एंटरप्राइज़ बैकएंड API सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। NestJS एंटरप्राइज़ बैकएंड API पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

कमांड क्यों मौजूद हैं

CQRS सिस्टम में हम मॉडल को दो हिस्सों में बाँटते हैं: राइट पक्ष, जो स्थिति बदलता है, और रीड पक्ष, जो डेटा लौटाता है। Command स्थिति बदलने के इरादे को दर्शाता है, जैसे CreateOrder या CancelSubscription।

  • कमांड एक सादा DTO होता है, जिसमें राइट कार्य करने के लिए आवश्यक डेटा होता है।
  • इसका नाम आज्ञार्थक रूप में होता है (यह करें) और यह एकल व्यावसायिक कार्रवाई का वर्णन करता है।
  • यह अधिकतम एक पहचानकर्ता या स्वीकृति लौटाता है, पूरा रीड मॉडल कभी नहीं।

NestJS में @nestjs/cqrs उपलब्ध है, जो हमें कमांड को ठीक एक हैंडलर तक भेजने के लिए CommandBus देता है।

कमांड का मॉडल बनाना

कमांड बस एक क्लास होती है। इसमें इनपुट डेटा readonly प्रॉपर्टी के रूप में रखा जाता है, ताकि बनने के बाद यह अपरिवर्तनीय रहे। इसमें कोई लॉजिक नहीं होता।

नीचे, CreateOrderCommand ऑर्डर बनाने के लिए हैंडलर को आवश्यक सभी चीज़ें समेटता है। ध्यान दें कि इसमें NestJS का कोई डेकोरेटर या व्यवहार नहीं है।

export class CreateOrderCommand {
  constructor(
    public readonly customerId: string,
    public readonly items: { sku: string; quantity: number }[],
    public readonly currency: string,
  ) {}
}

कमांड हैंडलर का अनुबंध

हर कमांड को ठीक एक हैंडलर प्रोसेस करता है। NestJS में आप ICommandHandler<TCommand> लागू करते हैं और क्लास को @CommandHandler(TCommand) से सजाते हैं।

  • @CommandHandler कमांड प्रकार और उसके हैंडलर के बीच संबंध पंजीकृत करता है।
  • इंटरफ़ेस में execute(command) मेथड होना अनिवार्य होता है।
  • हैंडलर एक सामान्य प्रोवाइडर होता है, इसलिए यह रिपॉज़िटरी और सेवाओं को इंजेक्ट कर सकता है।
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { CreateOrderCommand } from './create-order.command';

@CommandHandler(CreateOrderCommand)
export class CreateOrderHandler
  implements ICommandHandler<CreateOrderCommand>
{
  async execute(command: CreateOrderCommand): Promise<{ orderId: string }> {
    const orderId = crypto.randomUUID();
    // persist order, charge, etc.
    return { orderId };
  }
}

CommandBus के माध्यम से भेजना

कंट्रोलर और अन्य प्रवेश बिंदु हैंडलर को सीधे कॉल नहीं करते। वे एक कमांड बनाकर उसे CommandBus के माध्यम से execute() को सौंपते हैं। बस पंजीकृत हैंडलर को खोजकर चलाता है।

इस अप्रत्यक्ष व्यवस्था का अर्थ है कि कंट्रोलर सरल बना रहता है और उसे स्थायी संग्रहण, मान्यता नियमों या साइड इफ़ेक्ट्स के बारे में कुछ भी जानने की आवश्यकता नहीं होती।

import { Body, Controller, Post } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { CreateOrderCommand } from './create-order.command';

@Controller('orders')
export class OrdersController {
  constructor(private readonly commandBus: CommandBus) {}

  @Post()
  async create(@Body() dto: CreateOrderDto) {
    return this.commandBus.execute(
      new CreateOrderCommand(dto.customerId, dto.items, dto.currency),
    );
  }
}

CqrsModule से इसे जोड़ना

बस को हैंडलर खोजने में सक्षम बनाने के लिए, आपको फीचर मॉड्यूल में CqrsModule आयात करना होगा और प्रत्येक हैंडलर को प्रदाता के रूप में पंजीकृत करना होगा। NestJS स्टार्टअप के समय प्रदाताओं में @CommandHandler मेटाडेटा खोजता है और उन्हें बस से जोड़ता है।

  • providers में हैंडलर सूचीबद्ध करना भूल जाने पर, प्रेषण के समय बस अनहैंडल्ड कमांड त्रुटि उत्पन्न करता है।
  • CqrsModule, CommandBus, QueryBus और EventBus उपलब्ध कराता है।
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { OrdersController } from './orders.controller';
import { CreateOrderHandler } from './create-order.handler';

@Module({
  imports: [CqrsModule],
  controllers: [OrdersController],
  providers: [CreateOrderHandler],
})
export class OrdersModule {}

एक कमांड, एक हैंडलर

CommandBus एक सख्त एक-से-एक मैपिंग लागू करता है: एक कमांड प्रकार के लिए केवल एक हैंडलर निर्धारित होता है। यही इसे इवेंट्स से अलग करने वाली मूल बात है।

  • कमांड्स ऐसे निर्देश व्यक्त करते हैं जिन्हें अस्वीकार किया जा सकता है और जिनका ठीक एक हैंडलर होता है।
  • इवेंट्स यह बताते हैं कि कुछ पहले ही घटित हो चुका है और इनके शून्य या कई हैंडलर हो सकते हैं।

यदि आप एक ही कमांड के लिए दो हैंडलर पंजीकृत करते हैं, तो बाद में पंजीकृत हैंडलर प्रभावी हो जाता है और बस पहले वाले बंधन को चुपचाप बदल देती है। यह लगभग हमेशा एक गलती होती है।

कमांड से परिणाम लौटाना

सख्त CQRS विशेषज्ञ कमांड से कुछ भी लौटाने के पक्ष में नहीं होते। व्यवहार में, नए एग्रीगेट की आईडी जैसी छोटी स्वीकृति लौटाना NestJS API में व्यावहारिक और आम है।

CommandBus.execute<TCommand, TResult>() सामान्यीकृत है, इसलिए आप लौटाए जाने वाले मान का प्रकार सटीक रूप से निर्धारित कर सकते हैं। पेलोड छोटा रखें: आईडी या स्थिति, पूरा रीड मॉडल कभी नहीं। यदि क्लाइंट को पूरा संसाधन चाहिए, तो वह उसके बाद एक अलग क्वेरी भेजे।

const result = await this.commandBus.execute<
  CreateOrderCommand,
  { orderId: string }
>(new CreateOrderCommand(customerId, items, currency));

return { id: result.orderId };

मान्यता किनारे पर होनी चाहिए

हैंडलर को व्यावसायिक व्यवहार पर केंद्रित रखें। संरचनात्मक मान्यता (आवश्यक फ़ील्ड, प्रकार और प्रारूप) आने वाले DTO पर class-validator और वैश्विक ValidationPipe का उपयोग करके करें, इससे पहले कि कोई कमांड बनाया जाए।

ऐसे व्यावसायिक अपरिवर्तनीय नियम जिनके लिए डेटाबेस से डेटा चाहिए, जैसे 'ग्राहक की क्रेडिट सीमा पार नहीं होनी चाहिए', हैंडलर के अंदर होने चाहिए, जहाँ रिपॉज़िटरी तक आपकी पहुँच होती है।

import { IsArray, IsString, Length } from 'class-validator';

export class CreateOrderDto {
  @IsString()
  customerId: string;

  @IsArray()
  items: { sku: string; quantity: number }[];

  @IsString()
  @Length(3, 3)
  currency: string;
}

हैंडलर में साइड इफ़ेक्ट्स की रचना

एक हैंडलर आम तौर पर कई चरणों का समन्वय करता है: स्थिति लोड करना, उसमें बदलाव करना, उसे स्थायी रूप से सहेजना और फिर डोमेन इवेंट प्रकाशित करना। इंजेक्ट किए गए प्रदाता इस प्रक्रिया को साफ़-सुथरा और परीक्षण योग्य बनाते हैं।

यहाँ हैंडलर ऑर्डर को स्थायी रूप से सहेजता है और फिर एक इवेंट प्रकाशित करता है, ताकि सिस्टम के अन्य भाग असमकालिक रूप से प्रतिक्रिया दे सकें। इससे लेखन पथ ईमेल या विश्लेषण जैसी बाद की चिंताओं से अलग बना रहता है।

@CommandHandler(CreateOrderCommand)
export class CreateOrderHandler
  implements ICommandHandler<CreateOrderCommand>
{
  constructor(
    private readonly orders: OrderRepository,
    private readonly eventBus: EventBus,
  ) {}

  async execute(command: CreateOrderCommand) {
    const order = Order.create(command.customerId, command.items);
    await this.orders.save(order);
    this.eventBus.publish(new OrderCreatedEvent(order.id));
    return { orderId: order.id };
  }
}

शुद्ध तर्क का परीक्षण

क्योंकि कमांड एक साधारण अपरिवर्तनीय ऑब्जेक्ट होता है, इसलिए भेजने और संभालने के पैटर्न को समझना आसान होता है। नीचे दिया गया अंश किसी फ़्रेमवर्क पर निर्भर नहीं है: यह एक छोटा कमांड बस बनाता है, एक हैंडलर पंजीकृत करता है और एक कमांड भेजता है। इससे पहले देखे गए एक-कमांड-एक-हैंडलर अनुबंध का सटीक प्रदर्शन होता है।

type Handler<C, R> = (command: C) => R;

class MiniCommandBus {
  private handlers = new Map<string, Handler<any, any>>();

  register<C, R>(name: string, handler: Handler<C, R>): void {
    this.handlers.set(name, handler);
  }

  execute<R>(name: string, command: unknown): R {
    const handler = this.handlers.get(name);
    if (!handler) throw new Error(`No handler for ${name}`);
    return handler(command);
  }
}

class CreateOrderCommand {
  constructor(public readonly customerId: string) {}
}

const bus = new MiniCommandBus();
bus.register('CreateOrder', (c: CreateOrderCommand) => ({
  orderId: 'ord_' + c.customerId,
}));

const result = bus.execute<{ orderId: string }>(
  'CreateOrder',
  new CreateOrderCommand('42'),
);
console.log(result.orderId);

त्रुटि प्रबंधन और इडेम्पोटेंसी

कमांड विफल हो सकते हैं और कॉल करने वालों को स्पष्ट अनुबंध चाहिए। हैंडलर से डोमेन अपवाद उत्पन्न करें और NestJS के अपवाद फ़िल्टर को उन्हें HTTP स्थिति कोड में मैप करने दें।

  • execute() के अंदर अपवाद उत्पन्न करने पर commandBus.execute() द्वारा लौटाया गया प्रॉमिस अस्वीकृत हो जाता है।
  • कम-से-कम-एक-बार डिलीवरी (पुनःप्रयास और संदेश कतारों) के लिए कमांड को इडेम्पोटेंट बनाएँ: क्लाइंट द्वारा दी गई कुंजी शामिल करें, ताकि उसी कमांड को दोबारा संसाधित करना सुरक्षित रहे।

हैंडलर में त्रुटियों को कभी भी चुपचाप न दबाएँ; डिलीवर न हुआ लेखन क्लाइंट को सफल दिखाई देगा।

त्वरित जाँच

कमांड भेजने के मॉडल की अपनी समझ जाँचें।

पुनरावलोकन

अब आप जानते हैं कि NestJS CQRS में लेखन कार्यों को कमांड के रूप में कैसे मॉडल करना है:

  • कमांड एक अपरिवर्तनीय DTO होता है, जो स्थिति बदलने के आशय को व्यक्त करता है और जिसका नाम निर्देशात्मक शैली में रखा जाता है।
  • हैंडलर ICommandHandler को लागू करता है और @CommandHandler के माध्यम से संबद्ध होता है; इसमें व्यावसायिक तर्क और इंजेक्ट की गई निर्भरताएँ होती हैं।
  • CommandBus प्रत्येक कमांड को उसके एकमात्र हैंडलर तक भेजता है; कंट्रोलर सरल रहते हैं और हैंडलर को कभी सीधे कॉल नहीं करते।
  • CqrsModule आयात करें और हैंडलर को providers में पंजीकृत करें, ताकि बस उन्हें खोज सके।
  • संरचनात्मक मान्यता DTO पर, किनारे पर होती है; व्यावसायिक अपरिवर्तनीय नियम हैंडलर में होते हैं। केवल छोटी स्वीकृति लौटाएँ और जहाँ डिलीवरी दोबारा हो सकती हो, वहाँ कमांड को इडेम्पोटेंट बनाएँ।
शुरुआत निःशुल्क

एआई शिक्षक के साथ TypeScript सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
20
पाठ
76

अक्सर पूछे जाने वाले प्रश्न

क्या “कमांड, हैंडलर और CommandBus” पाठ निःशुल्क है?

हाँ—“कमांड, हैंडलर और CommandBus” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और NestJS एंटरप्राइज़ बैकएंड API पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। NestJS एंटरप्राइज़ बैकएंड API पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“कमांड, हैंडलर और CommandBus” में मैं क्या सीखूँगा?

लेखन कार्यों को कमांड के रूप में मॉडल कीजिए और उन्हें CommandBus के माध्यम से समर्पित हैंडलरों तक भेजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ NestJS एंटरप्राइज़ बैकएंड API का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या NestJS एंटरप्राइज़ बैकएंड API शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर NestJS एंटरप्राइज़ बैकएंड API शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 1वाँ पाठ है।

“कमांड, हैंडलर और CommandBus” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस NestJS एंटरप्राइज़ बैकएंड API पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर NestJS एंटरप्राइज़ बैकएंड API पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. कमांड, हैंडलर और CommandBus
  2. क्वेरी और रीड-मॉडल प्रक्षेपण
  3. डोमेन इवेंट और AggregateRoot
  4. लंबे समय तक चलने वाले कार्यप्रवाहों के लिए Saga
← NestJS एंटरप्राइज़ बैकएंड API पर वापस जाएँ