Ответ
В моем последнем проекте мы использовали монолитную архитектуру на Express.js, где все компоненты были в одном репозитории и процессе:
Структура проекта:
monolith-app/
├── src/
│ ├── controllers/ # Обработчики маршрутов
│ ├── models/ # Модели данных (Mongoose/Squelize)
│ ├── routes/ # Определение маршрутов
│ ├── middleware/ # Промежуточное ПО
│ ├── services/ # Бизнес-логика
│ ├── utils/ # Вспомогательные функции
│ └── app.js # Основной файл приложения
├── package.json
└── .env
Пример app.js:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
// Подключение к MongoDB
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// Маршруты
app.use('/api/users', require('./src/routes/userRoutes'));
app.use('/api/products', require('./src/routes/productRoutes'));
app.use('/api/orders', require('./src/routes/orderRoutes'));
// Пример маршрута с полным стеком в одном месте
app.get('/api/dashboard', async (req, res) => {
try {
// 1. Получаем данные из БД
const userCount = await User.countDocuments();
const productCount = await Product.countDocuments();
const recentOrders = await Order.find()
.sort({ createdAt: -1 })
.limit(10)
.populate('user');
// 2. Применяем бизнес-логику
const growthRate = await calculateGrowthRate();
// 3. Формируем ответ
res.json({
users: userCount,
products: productCount,
recentOrders,
growthRate,
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Dashboard error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Запуск сервера
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Monolithic app running on port ${PORT}`);
});
Преимущества, которые я наблюдал:
- Быстрый старт — один репозиторий, одна кодовая база
- Простая отладка — все логи в одном процессе, стектрейсы полные
- Сквозная типизация — если используется TypeScript, типы доступны везде
- Общие ресурсы — одно подключение к БД, общий кэш в памяти
Проблемы, с которыми столкнулся:
- Разрастание кодовой базы — через год было 50+ маршрутов и 30+ моделей
- Сложность деплоя — обновление одной функции требовало перезапуска всего приложения
- Масштабирование — приходилось масштабировать весь монолит, даже если нагрузка была только на один модуль
Мы начали выделять модули в отдельные сервисы, когда ежедневная нагрузка превысила 100k запросов.