Микросервисы на TypeScript: архитектура и практика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем TypeScript в микросервисах?

Микросервисная архитектура стала стандартом для построения масштабируемых и отказоустойчивых приложений. Однако выбор языка программирования может существенно повлиять на скорость разработки и качество кода. TypeScript — строго типизированное надмножество JavaScript — предлагает идеальный баланс между гибкостью и надёжностью. Благодаря статической типизации, интерфейсам и мощной системе модулей, TypeScript позволяет выявлять ошибки на этапе компиляции, а не в рантайме, что критически важно для распределённых систем.

В этой статье мы разберём ключевые принципы построения микросервисов на TypeScript, рассмотрим популярные фреймворки (NestJS, Fastify), настройку межсервисного взаимодействия через RabbitMQ, а также контейнеризацию с Docker. Все примеры кода будут представлены на TypeScript, чтобы вы могли сразу применить их в своих проектах.

1. Выбор фреймворка: NestJS vs Fastify

Для создания микросервисов на TypeScript существует несколько фреймворков. Два самых популярных — NestJS и Fastify. Рассмотрим их особенности.

NestJS — «Angular для бэкенда»

NestJS предлагает модульную архитектуру, вдохновлённую Angular. Он поставляется с готовым транспортным слоем для микросервисов (TCP, Redis, RabbitMQ, Kafka). Это идеальный выбор для крупных проектов, где важна структурированность и поддержка командой разработчиков.

// Пример базового микросервиса на NestJS
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.RMQ,
      options: {
        urls: ['amqp://localhost:5672'],
        queue: 'orders_queue',
        queueOptions: { durable: false },
      },
    },
  );
  await app.listen();
}
bootstrap();

Fastify — производительность и простота

Fastify — это сверхбыстрый HTTP-фреймворк для Node.js. Он не предоставляет встроенной поддержки микросервисов, но легко интегрируется с RabbitMQ или NATS через отдельные пакеты. Подходит для случаев, когда производительность критична, а архитектура уже определена.

// Пример сервера на Fastify + TypeScript
import fastify from 'fastify';

const server = fastify({ logger: true });

server.get('/health', async (request, reply) => {
  return { status: 'OK', service: 'payment-service' };
});

const start = async () => {
  try {
    await server.listen({ port: 3000 });
  } catch (err) {
    server.log.error(err);
    process.exit(1);
  }
};
start();

Вывод: для начинающих и командных проектов выбирайте NestJS. Для высоконагруженных систем с уже продуманной архитектурой — Fastify.

2. Межсервисное взаимодействие: RabbitMQ и Redis

В микросервисной архитектуре сервисы общаются друг с другом через брокеры сообщений или прямые HTTP-запросы. Рассмотрим два популярных подхода.

RabbitMQ — надёжная очередь сообщений

RabbitMQ обеспечивает асинхронную передачу сообщений с гарантией доставки. В TypeScript для работы с RabbitMQ часто используют библиотеку amqplib или встроенные модули NestJS.

// Публикация сообщения в RabbitMQ (TypeScript)
import * as amqp from 'amqplib';

async function publishOrder(order: any) {
  const connection = await amqp.connect('amqp://localhost');
  const channel = await connection.createChannel();
  const queue = 'orders';

  await channel.assertQueue(queue, { durable: true });
  channel.sendToQueue(queue, Buffer.from(JSON.stringify(order)), {
    persistent: true,
  });

  console.log(`Order sent: ${order.id}`);
  setTimeout(() => connection.close(), 500);
}

publishOrder({ id: 123, items: ['book', 'laptop'] });

Redis Pub/Sub — для скорости

Если вам не нужна гарантированная доставка, а важна низкая задержка, используйте Redis Pub/Sub. Он идеально подходит для уведомлений и событий реального времени.

// Подписка на канал Redis в TypeScript
import { createClient } from 'redis';

const subscriber = createClient();
await subscriber.connect();

await subscriber.subscribe('order_events', (message) => {
  console.log('Received event:', JSON.parse(message));
});

3. Архитектура проекта и организация кода

Правильная структура проекта — залог поддерживаемости. Рекомендуем использовать монорепозиторий с NX или Lerna. Вот примерная структура для микросервисов на TypeScript:

monorepo/
├── packages/
│   ├── order-service/       # Микросервис заказов
│   │   ├── src/
│   │   │   ├── controllers/
│   │   │   ├── services/
│   │   │   ├── models/
│   │   │   └── main.ts
│   │   └── package.json
│   ├── payment-service/     # Микросервис платежей
│   │   ├── src/
│   │   └── package.json
│   └── shared/              # Общие типы и утилиты
│       ├── src/
│       │   ├── interfaces/
│       │   └── dto/
│       └── package.json
├── docker-compose.yml
└── tsconfig.base.json

Ключевые принципы:

  • Единое хранилище типов — все интерфейсы и DTO выносятся в пакет shared, чтобы избежать дублирования.
  • Слабая связанность — каждый сервис использует только свой порт и не зависит от прямого вызова других сервисов.
  • Docker Compose — поднимайте все сервисы и инфраструктуру одной командой.

4. Деплой и контейнеризация с Docker

Каждый микросервис должен быть упакован в отдельный Docker-образ. Для TypeScript сначала компилируем код в JavaScript, затем собираем лёгкий образ на основе node:18-alpine.

# Dockerfile для TypeScript микросервиса
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules                

Блоги

Рекомендаций по книгам