Ответ
Одной из самых сложных задач была миграция монолитного приложения на Express.js с 500k строк кода на микросервисную архитектуру без downtime для пользователей.
Вызовы:
- Распутывание зависимостей: Монолит имел запутанные модульные связи и общее состояние в памяти.
- Согласованность данных: Необходимо было разделить общую PostgreSQL базу между новыми сервисами, сохраняя целостность транзакций.
- Коммуникация: Организация взаимодействия между 10+ новыми сервисами (ордеры, пользователи, платежи и т.д.).
Мое решение и реализация:
1. Стратегия «Strangler Fig»:
- Мы не переписывали всё сразу. Я создал API Gateway (на Node.js с
@nestjs/platform-express), который перенаправлял трафик либо в старый монолит, либо в новые сервисы. - Постепенно мы «отрезали» модули от монолита, превращая их в независимые сервисы (например, первым вынесли сервис нотификаций на Nest.js с RabbitMQ).
2. Синхронизация данных через события:
- Для замены общих JOIN-запросов внедрили механизм событийной синхронизации. При изменении сущности в «сервисе пользователей» он публиковал событие в Kafka.
-
Другие сервисы (например, «сервис ордеров») подписывались на эти события и поддерживали свою денормализованную копию необходимых данных в своей БД.
// Пример публикации события в сервисе пользователей (Nest.js) @Injectable() export class UserService { constructor(private kafkaProducer: KafkaProducerService) {} async updateUser(userId: string, data: UpdateUserDto) { const updatedUser = await this.userRepo.update(userId, data); // Публикуем событие для других сервисов await this.kafkaProducer.emit('user.updated', { id: updatedUser.id, email: updatedUser.email, updatedAt: new Date().toISOString() }); return updatedUser; } }
3. Управление распределенными транзакциями:
- Для сценариев, требующих согласованности (например, создание заказа со списанием денег), реализовали паттерн Saga с компенсирующими транзакциями, используя состояние в PostgreSQL.
Итог: Миграция заняла 9 месяцев, но прошла незаметно для конечных пользователей. В результате мы добились независимого масштабирования сервисов, повысили отказоустойчивость и ускорили циклы разработки новых фич.