Prowid Photos растёт: вместо двух разработчиков уже шесть, а в планах тридцать. На созвоне кто-то говорит: «Давайте перейдём на микросервисы — так делают Netflix и Amazon». Звучит убедительно, но честный ответ — «зависит». В этом уроке разберём три способа собрать приложение — монолит, модульный монолит и микросервисы, — что даёт и чего стоит каждый, как мигрировать без остановки и что выбрать для Prowid Photos.
Монолит: всё приложение целиком
Монолит — это одно приложение, которое разворачивают целиком: один процесс, один репозиторий, обычно одна база данных. Сейчас API Prowid Photos именно такой: одно Express-приложение с разделами users, photos, feed, follows и notifications.
Для старта это отличный выбор:
- Просто запускать и разворачивать: одна команда, один Docker-образ.
- Вызов — это вызов функции: занимает наносекунды (лестница задержек из первого урока) и не может «потеряться в сети».
- Одна база — одна транзакция: загрузка фото и увеличение счётчика либо произойдут вместе, либо не произойдут вовсе.
- Просто отлаживать и тестировать: один стек вызовов, один лог, дёшево.
// the monolith: both tables live in one database, so one transaction is enough
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query(
'INSERT INTO photos (id, user_id, url) VALUES ($1, $2, $3)',
[photoId, userId, url],
);
await client.query(
'UPDATE users SET photos_count = photos_count + 1 WHERE id = $1',
[userId],
);
await client.query('COMMIT'); // both changes happen, or neither
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}Проблемы приходят с ростом. Код превращается в «большой ком грязи» (big ball of mud): всё импортирует всё, и правка уведомлений ломает ленту. Любое мелкое изменение требует развернуть и перетестировать всё приложение. Масштабировать можно только целиком, даже если нагружена одна часть. Команды наступают друг другу на ноги в одном репозитории.
Модульный монолит: стены внутри дома
Представьте один дом. В монолите — одна огромная комната, где все живут вместе. В модульном монолите те же стены, но внутри есть комнаты с дверями. Приложение остаётся одним процессом и одним деплоем, зато код разделён на модули по областям бизнеса с жёсткими правилами:
- у модуля есть публичное API — один файл
index.ts, который экспортирует несколько функций и событий; всё остальное внутри скрыто; - другие модули импортируют только публичное API, а не файлы внутри;
- у модуля свои таблицы (схема), и чужие таблицы он не читает через
JOIN; - модули общаются вызовами функций или событиями внутри процесса.
src/modules/
photos/
index.ts <- the public API: the ONLY file others import
photos.service.ts
photos.repository.ts
users/
feed/
notifications/