Какие паттерны разработки знаешь и применял в Node.js?

«Какие паттерны разработки знаешь и применял в Node.js?» — вопрос из категории Паттерны, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

  1. Middleware / Chain of Responsibility: Основа большинства веб-фреймворков (Express, Koa). Позволяет декомпозировать обработку запроса.

    // Express middleware для логирования и аутентификации
    const logger = (req, res, next) => {
      console.log(`${req.method} ${req.url}`);
      next();
    };
    const auth = (req, res, next) => {
      const token = req.headers.authorization;
      if (!isValidToken(token)) return res.status(401).send('Unauthorized');
      req.user = decodeToken(token);
      next();
    };
    app.use(logger);
    app.use('/api', auth); // Применяется только к маршрутам /api/*
  2. Repository / Data Mapper: Для абстракции работы с базой данных. Бизнес-логика работает с "репозиториями", а не с конкретными ORM-запросами.

    // user.repository.js
    class UserRepository {
      constructor(model) { this.model = model; }
      async findById(id) { return this.model.findById(id); }
      async findByEmail(email) { return this.model.findOne({ email }); }
    }
    // В сервисе
    const userRepo = new UserRepository(UserModel);
    const user = await userRepo.findByEmail('test@example.com');
  3. Dependency Injection (DI): Улучшает тестируемость. Вместо жёсткого импорта модулей, зависимости передаются в конструктор или функцию.

    // Сервис, принимающий репозиторий как зависимость
    class UserService {
      constructor(userRepository, emailService) {
        this.userRepo = userRepository;
        this.emailService = emailService;
      }
      async register(userData) {
        const user = await this.userRepo.create(userData);
        await this.emailService.sendWelcomeEmail(user.email);
        return user;
      }
    }
    // В тестах легко подменить репозиторий на mock
  4. Factory: Для создания сложных объектов, например, разных типов подключений или валидаторов.

    function createDatabaseConnection(type) {
      if (type === 'mongodb') return new MongoClient(uri);
      if (type === 'redis') return new RedisClient();
      throw new Error(`Unsupported DB type: ${type}`);
    }
    const db = createDatabaseConnection(process.env.DB_TYPE);
  5. Observer / Event Emitter: Встроен в Node.js через класс EventEmitter. Используется для организации слабосвязанной коммуникации между частями приложения.

    const EventEmitter = require('events');
    class OrderService extends EventEmitter {
      async processOrder(order) {
        // ... логика обработки
        this.emit('order.processed', { orderId: order.id });
      }
    }
    const orderService = new OrderService();
    orderService.on('order.processed', (data) => {
      // Отправить уведомление, обновить кэш и т.д.
      console.log(`Order ${data.orderId} processed`);
    });
  6. Module Pattern / Singleton (через кэш модулей CommonJS): Node.js сам реализует Singleton для модулей. При первом require() модуль кэшируется, и последующие вызовы возвращают тот же экземпляр.

Эти паттерны помогают создавать поддерживаемые, тестируемые и масштабируемые приложения на Node.js.