Микросервисы на TypeScript: ваш первый шаг к модульной архитектуре
Представьте, что вы строите дом. Можно возвести одну огромную стену, а затем добавлять к ней комнаты. Но если одна комната сломается — пострадает всё здание. Альтернатива — построить отдельные модули: кухню, спальню, гостиную, каждый со своей проводкой и сантехникой. Примерно так работают микросервисы — независимые, слабо связанные компоненты, которые общаются через сеть.
TypeScript для микросервисов — это не просто модный тренд, а инструмент, который спасает от хаоса. Статическая типизация отлавливает ошибки на этапе компиляции, а не в продакшене. В этой статье мы разберём, с чего начать, если вы хотите перейти от монолита к микросервисам на TypeScript. Без воды, с реальными примерами кода.
Почему TypeScript, а не чистый JavaScript?
Когда вы пишете микросервисы, каждый сервис — это отдельный проект. Без типов легко запутаться: какой формат данных ожидает сервис A, что возвращает сервис B? TypeScript решает эту проблему:
- Интерфейсы и типы — вы чётко определяете контракты между сервисами.
- Автокомплит и рефакторинг — IDE подсказывает поля объектов, а переименование не ломает код.
- Совместимость с Node.js — TypeScript компилируется в JavaScript, работает везде, где есть Node.
Пример: представьте, что сервис заказов отправляет данные сервису доставки. Без типов вы можете случайно передать строку вместо числа. TypeScript скажет: «Эй, тут ожидается number, а ты шлёшь string».
Инструментарий для старта
Чтобы начать, вам понадобятся:
- Node.js (версия 18+) — среда выполнения.
- TypeScript — устанавливаем глобально или в проекте.
- NestJS — фреймворк для серверных приложений. Он из коробки поддерживает микросервисную архитектуру.
- RabbitMQ или NATS — брокеры сообщений для асинхронного общения.
- Docker — контейнеризация каждого сервиса.
Не бойтесь, всё это настраивается за 15 минут.
Шаг 1: Создаём первый микросервис с NestJS
NestJS — это как Angular для бэкенда. Он использует декораторы, модули и провайдеры. Установим его:
npm i -g @nestjs/cli
nest new user-service
cd user-service
npm install @nestjs/microservices
Теперь превратим обычное приложение в микросервис. Откроем main.ts:
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.TCP, // или Redis, RabbitMQ
options: {
host: '0.0.0.0',
port: 3001,
},
},
);
await app.listen();
console.log('User microservice is listening on port 3001');
}
bootstrap();
Мы создали микросервис, который слушает TCP-порт 3001. Теперь добавим обработчик сообщений в app.controller.ts:
import { Controller } from '@nestjs/common';
import { MessagePattern, Payload } from '@nestjs/microservices';
@Controller()
export class AppController {
@MessagePattern('get_user')
getUser(@Payload() data: { id: number }) {
// Имитация запроса к БД
return { id: data.id, name: 'Alice', email: 'alice@example.com' };
}
}
Всё! Первый микросервис готов. Он принимает сообщение с паттерном get_user и возвращает данные пользователя.
Шаг 2: Создаём второй сервис и организуем общение
Сделаем ещё один сервис — order-service. Он будет запрашивать данные у user-service. Создадим его аналогично, но изменим порт на 3002.
В order-service установим клиент для общения:
npm install @nestjs/microservices
Теперь в контроллере заказов напишем вызов к user-service:
import { Controller, Get } from '@nestjs/common';
import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices';
@Controller('orders')
export class OrdersController {
private client: ClientProxy;
constructor() {
this.client = ClientProxyFactory.create({
transport: Transport.TCP,
options: { host: 'localhost', port: 3001 },
});
}
@Get(':id')
async getOrderWithUser() {
// Отправляем сообщение и ждём ответ
const user = await this.client.send('get_user', { id: 1 }).toPromise();
return { order: 'Book', user };
}
}
Запустите оба сервиса в разных терминалах. При запросе к http://localhost:3002/orders/1 order-service получит данные от user-service. Магия микросервисов в действии!
Шаг 3: Добавляем RabbitMQ для асинхронности
TCP — хорошо, но если сервис временно недоступен, запрос потеряется. Для надёжности используйте брокеры сообщений. RabbitMQ — популярный выбор.
Установите RabbitMQ через Docker:
docker run -d --name rabbitmq -p 5672:5672 rabbitmq:3-management
Теперь переключим user-service на RabbitMQ. В main.ts меняем транспорт:
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.RMQ,
options: {
urls: ['amqp://localhost:5672'],
queue: 'user_queue',
queueOptions: { durable: false },
},
},
);
В order-service обновляем клиент:
this.client = ClientProxyFactory.create({
transport: Transport.RMQ,
options: {
urls: ['amqp://localhost:5672'],
queue: 'user_queue',
},
});
Теперь сообщения не теряются: если user-service упадёт, RabbitMQ сохранит их до восстановления.
Шаг 4: Контейнеризация с Docker Compose
Микросервисы должны быть изолированы. Docker Compose поднимает всё одной командой. Создайте docker-compose.yml в корне:
version: '3.8'
services:
user-service:
build: ./user-service
ports:
- "3001:3001"
depends_on:
- rabbitmq
order-service:
build: ./order-service
ports:
- "3002:3002"
depends_on:
- rabbitmq
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"