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
bashnpm install @nestjs/microservices
Create Microservice
typescriptimport { 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)
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
typescriptimport { 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
- Service Boundaries: Clearly define service boundaries and responsibilities
- Asynchronous Communication: Use asynchronous messaging for inter-service communication
- Idempotency: Ensure operations are idempotent and can be safely retried
- Error Handling: Implement appropriate error handling and retry mechanisms
- Monitoring: Implement comprehensive monitoring and logging
- Configuration Management: Use configuration centers to manage service configurations
- Version Control: Implement API versioning strategies
- 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.