乐闻世界logo
搜索文章和话题

How to design NestJS microservices and architecture?

2月17日 22:33

Microservices Architecture Overview

Microservices architecture is an approach to building applications as a set of small, independent services, each running in its own process and communicating through lightweight mechanisms (typically HTTP APIs or message queues). NestJS provides comprehensive microservices support, enabling developers to easily build scalable distributed systems.

NestJS Microservices Basics

Install Dependencies

bash
npm install @nestjs/microservices

Create Microservice

typescript
import { NestFactory } from '@nestjs/core'; import { Transport, MicroserviceOptions } from '@nestjs/microservices'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.createMicroservice<MicroserviceOptions>( AppModule, { transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877, }, }, ); await app.listen(); } bootstrap();

Hybrid Application (HTTP + Microservice)

typescript
import { NestFactory } from '@nestjs/core'; import { Transport, MicroserviceOptions } from '@nestjs/microservices'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); const microservice = app.connectMicroservice<MicroserviceOptions>({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877, }, }); await app.startAllMicroservices(); await app.listen(3000); } bootstrap();

Messaging Patterns

1. Message Pattern

typescript
import { Controller } from '@nestjs/common'; import { EventPattern, Payload, Ctx, RmqContext } from '@nestjs/microservices'; @Controller() export class MathController { @MessagePattern({ cmd: 'sum' }) accumulate(@Payload() data: number[]): number { return (data || []).reduce((a, b) => a + b, 0); } }

2. Event Pattern

typescript
import { Controller } from '@nestjs/common'; import { EventPattern, Payload } from '@nestjs/microservices'; @Controller() export class NotificationController { @EventPattern('user_created') async handleUserCreated(@Payload() data: Record<string, unknown>) { // Handle user created event } }

Transport Layers

1. TCP Transport

typescript
{ transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877, }, }

2. Redis Transport

typescript
{ transport: Transport.REDIS, options: { host: 'localhost', port: 6379, }, }

3. NATS Transport

typescript
{ transport: Transport.NATS, options: { url: 'nats://localhost:4222', }, }

4. MQTT Transport

typescript
{ transport: Transport.MQTT, options: { url: 'mqtt://localhost:1883', }, }

5. RabbitMQ Transport

typescript
{ transport: Transport.RMQ, options: { urls: ['amqp://localhost:5672'], queue: 'cats_queue', queueOptions: { durable: false, }, }, }

6. Kafka Transport

typescript
{ transport: Transport.KAFKA, options: { client: { brokers: ['localhost:9092'], }, consumer: { groupId: 'my-consumer', }, }, }

Client

Create Client Proxy

typescript
import { Controller, Get } from '@nestjs/common'; import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices'; @Controller() export class AppController { private client: ClientProxy; constructor() { this.client = ClientProxyFactory.create({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877, }, }); } @Get() async getSum() { return this.client.send({ cmd: 'sum' }, [1, 2, 3, 4, 5]); } }

Configure Client Using Module

typescript
import { Module } from '@nestjs/common'; import { ClientsModule, Transport } from '@nestjs/microservices'; @Module({ imports: [ ClientsModule.register([ { name: 'MATH_SERVICE', transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877, }, }, ]), ], controllers: [AppController], }) export class AppModule {}

Inject Client

typescript
import { Controller, Get, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; @Controller() export class AppController { constructor(@Inject('MATH_SERVICE') private client: ClientProxy) {} @Get() async getSum() { return this.client.send({ cmd: 'sum' }, [1, 2, 3, 4, 5]); } }

Message Acknowledgment

Manual Acknowledgment

typescript
import { Controller } from '@nestjs/common'; import { EventPattern, Payload, Ctx, RmqContext } from '@nestjs/microservices'; @Controller() export class NotificationController { @EventPattern('notification_created') async handleNotificationCreated( @Payload() data: any, @Ctx() context: RmqContext, ) { const channel = context.getChannelRef(); const originalMsg = context.getMessage(); // Process message await this.processNotification(data); // Manual acknowledgment channel.ack(originalMsg); } }

Configure Prefetch Count

typescript
{ transport: Transport.RMQ, options: { urls: ['amqp://localhost:5672'], queue: 'notifications_queue', prefetchCount: 10, queueOptions: { durable: false, }, }, }

Microservices Architecture Patterns

1. API Gateway Pattern

typescript
import { Controller, Get, Param, Post, Body } from '@nestjs/common'; import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices'; @Controller('api') export class ApiController { private userService: ClientProxy; private orderService: ClientProxy; constructor() { this.userService = ClientProxyFactory.create({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 8877 }, }); this.orderService = ClientProxyFactory.create({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 8878 }, }); } @Get('users/:id') getUser(@Param('id') id: string) { return this.userService.send({ cmd: 'get_user' }, { id }); } @Post('orders') createOrder(@Body() orderData: any) { return this.orderService.send({ cmd: 'create_order' }, orderData); } }

2. Event-Driven Architecture

typescript
// Order service @Controller() export class OrderController { @EventPattern('order_created') async handleOrderCreated(@Payload() order: any) { // Handle order created event await this.sendNotification(order); await this.updateInventory(order); } } // Notification service @Controller() export class NotificationController { @EventPattern('order_created') async handleOrderCreated(@Payload() order: any) { // Send notification await this.sendEmail(order.userEmail, 'Order created'); } } // Inventory service @Controller() export class InventoryController { @EventPattern('order_created') async handleOrderCreated(@Payload() order: any) { // Update inventory await this.reduceStock(order.items); } }

3. CQRS Pattern

typescript
// Command Handler @Controller() export class OrderCommandController { @MessagePattern({ cmd: 'create_order' }) async createOrder(@Payload() command: CreateOrderCommand) { return this.commandBus.execute(command); } } // Query Handler @Controller() export class OrderQueryController { @MessagePattern({ cmd: 'get_order' }) async getOrder(@Payload() query: GetOrderQuery) { return this.queryBus.execute(query); } }

Service Discovery

Using Consul

typescript
import { Module } from '@nestjs/common'; import { ClientsModule, Transport } from '@nestjs/microservices'; @Module({ imports: [ ClientsModule.register([ { name: 'USER_SERVICE', transport: Transport.TCP, options: { host: 'user-service', port: 3000, }, }, ]), ], }) export class AppModule {}

Using Kubernetes Service Discovery

typescript
{ transport: Transport.TCP, options: { host: process.env.USER_SERVICE_HOST || 'user-service', port: parseInt(process.env.USER_SERVICE_PORT) || 3000, }, }

Distributed Tracing

Integrate OpenTelemetry

typescript
import { Controller } from '@nestjs/common'; import { MessagePattern, Payload } from '@nestjs/microservices'; import { trace } from '@opentelemetry/api'; @Controller() export class OrderController { @MessagePattern({ cmd: 'create_order' }) async createOrder(@Payload() data: any) { const tracer = trace.getTracer('order-service'); const span = tracer.startSpan('create_order'); try { const result = await this.orderService.create(data); span.end(); return result; } catch (error) { span.recordException(error); span.end(); throw error; } } }

Best Practices

  1. Service Boundaries: Clearly define service boundaries and responsibilities
  2. Asynchronous Communication: Use asynchronous messaging for inter-service communication
  3. Idempotency: Ensure operations are idempotent and can be safely retried
  4. Error Handling: Implement appropriate error handling and retry mechanisms
  5. Monitoring: Implement comprehensive monitoring and logging
  6. Configuration Management: Use configuration centers to manage service configurations
  7. Version Control: Implement API versioning strategies
  8. Testing: Write integration tests and contract tests

Summary

NestJS microservices and architecture provide:

  • Complete microservices support
  • Multiple transport layer options
  • Flexible messaging patterns
  • Powerful client proxies
  • Easy to build distributed systems

Mastering NestJS microservices architecture is key to building scalable, maintainable enterprise applications. By properly using microservices patterns, event-driven architecture, and best practices, you can build high-performance, reliable distributed systems. Microservices architecture enables teams to independently develop, deploy, and scale individual services, improving development efficiency and system resilience.

标签:NestJS