С какими паттернами проектирования вы работали в Node.js?

Ответ

В Node.js-проектах я активно применял следующие паттерны:

  1. Middleware / Цепочка обязанностей (Chain of Responsibility): Фундаментальный паттерн в Express/Koa для обработки запросов.

    // Кастомный middleware для логирования и проверки прав
    const logger = (req, res, next) => {
        console.log(`${req.method} ${req.url}`);
        next();
    };
    app.use(logger);
  2. Singleton (Одиночка): Для управления единственным подключением к базе данных или кэшу.

    // db.js - Singleton для подключения к Redis
    const Redis = require('ioredis');
    class RedisConnection {
        constructor() {
            if (!RedisConnection.instance) {
                RedisConnection.instance = new Redis(process.env.REDIS_URL);
            }
        }
        getInstance() {
            return RedisConnection.instance;
        }
    }
    module.exports = new RedisConnection();
  3. Observer / EventEmitter: Встроенная в Node.js событийная модель, которую я использовал для декомпозиции логики (например, отправка email после регистрации).

    const EventEmitter = require('events');
    const orderEmitter = new EventEmitter();
    orderEmitter.on('order.created', (orderData) => {
        // Отправка уведомления, обновление инвентаря
        sendEmail(orderData.userEmail);
    });
  4. Repository (Репозиторий): Для абстрагирования слоя доступа к данным, что упрощает тестирование и смену БД.

    // userRepository.js
    class UserRepository {
        constructor(db) { this.db = db; }
        async findById(id) {
            return this.db.user.findUnique({ where: { id } });
        }
    }
  5. Factory (Фабрика): Для создания сложных объектов, например, различных типов валидаторов или клиентов для внешних API (Stripe, SendGrid).

  6. Dependency Injection (Внедрение зависимостей): Часто применяю в связке с Repository паттерном для написания модульных и тестируемых сервисов.

Ответ 18+ 🔞

А, паттерны в Node.js, говоришь? Ну, это как с машиной — можно ездить на старой развалюхе, а можно на нормальной тачке, где всё по полочкам разложено. Я тебе щас расскажу, как я обычно делаю, чтобы проект не превращался в пиздопроебибну, где через полгода сам чёрт ногу сломит.

1. Middleware / Цепочка обязанностей Это, блядь, основа основ в Express или Koa. Представь, что запрос — это пацан, который идёт по коридору, а каждый middleware — это дверь, в которую ему нужно постучаться. Один проверяет, есть ли у него пропуск (авторизация), другой пишет в журнал, кто пришёл (логирование), третий смотрит, не несёт ли он с собой гранату (валидация). Если на каком-то этапе его посылают нахуй — дальше он не идёт. Красота, ёпта.

// Кастомный middleware для логирования и проверки прав
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next(); // Говорим: "Иди дальше, чувак, ты чист"
};
app.use(logger);

2. Singleton (Одиночка) Это когда тебе нужно, чтобы какая-то хуйня была в единственном экземпляре. Например, подключение к базе данных. Представь, что у тебя в приложении 100 раз создаётся новое подключение к Redis — это же ёперный театр, сервер сдохнет от нагрузки. Singleton говорит: "Расслабься, братан, я уже всё создал, бери и пользуйся". Доверия к нему, правда, ебать ноль, потому что если он накроется, то всё приложение встанет. Но зато красиво.

// db.js - Singleton для подключения к Redis
const Redis = require('ioredis');
class RedisConnection {
    constructor() {
        if (!RedisConnection.instance) {
            RedisConnection.instance = new Redis(process.env.REDIS_URL);
        }
    }
    getInstance() {
        return RedisConnection.instance;
    }
}
module.exports = new RedisConnection(); // Вот он, один на весь проект

3. Observer / EventEmitter Это моя любимая тема. Встроенная в Node.js фишка, просто золото. Допустим, пользователь зарегистрировался. Тебе нужно: записать в БД, отправить приветственное письмо, начислить бонусы, запустить фейерверк. Если всё это пихать в один жирный метод, получится хитрая жопа, которую невозможно поддерживать. А тут ты просто кричишь на весь проект: "Эй, народ, у нас новый юзер!" И кто хочет — тот и реагирует. Один слушатель письма шлёт, другой — бонусы начисляет. Развязка полная.

const EventEmitter = require('events');
const orderEmitter = new EventEmitter();
// Где-то в другом файле, который про email
orderEmitter.on('order.created', (orderData) => {
    sendEmail(orderData.userEmail); // Отправляем письмо и не паримся
});

4. Repository (Репозиторий) Это когда ты прячешь всю работу с базой данных в отдельный класс. Зачем? Ну, представь, что сегодня ты используешь PostgreSQL, а завтра начальник говорит: "Всё, переезжаем на MongoDB, ядрёна вошь!" Если у тебя SQL-запросы раскиданы по всем сервисам, ты просто сдохнешь. А если есть репозиторий, ты меняешь логику только в одном месте. И тестировать в разы проще — подсовываешь ему заглушку вместо реальной БД и проверяешь логику. Умно, да?

// userRepository.js
class UserRepository {
    constructor(db) { this.db = db; }
    async findById(id) {
        return this.db.user.findUnique({ where: { id } });
    }
}
// В сервисе ты просто вызываешь repo.findById(123) и не думаешь, как это работает внутри.

5. Factory (Фабрика) Это такая мастерская по производству объектов. Допустим, у тебя три типа уведомлений: email, SMS, push. Писать везде if (type === 'email') ... else if ... — это моветон, чувак. Создаёшь фабрику, говоришь: "Мне уведомление типа 'email'", и она тебе возвращает готовый, настроенный объект, который уже умеет отправлять письма. Красота, ни хуя себе как удобно.

6. Dependency Injection (Внедрение зависимостей) Самый, блядь, важный паттерн для тестирования. Ты не создаёшь зависимости (типа того же репозитория) внутри класса, а просишь: "Дай мне сюда базу". Это как если бы ты не покупал водку в баре, а приносил свою. Так проще контролировать, что именно ты пьёшь. В тестах ты подсовываешь классу заглушку (mock), и он даже не заметит подмены. Всё становится модульным и прозрачным. Без этого писать нормальные unit-тесты — это хуй с горы, бесполезное занятие.

Вот так, коротко и по делу. Главное — не переусердствовать. Иногда простой скрипт из одного файла не нужно обкладывать всеми этими паттернами, а то получится манда с ушами. Но для серьёзных проектов — это must have, иначе потом будешь сам от себя охуевать, разбираясь в своём же коде.