Структура Node.js проекта и слои

Разделение кода на слои (config, lib, api, domain, db) — фундамент поддерживаемого приложения. Смешение слоёв в одном файле — главный антипаттерн Node-проектов.

Суть

В простом случае всё работает в одном index.js: HTTP, парсинг тела, обращение к БД, формирование ответа, логирование. Через 2 месяца этот файл — 3000 строк говнокода. Решение — разнести по слоям:

project/
├─ package.json          # точка входа, скрипты, зависимости
├─ .nvmrc                # версия Node
├─ .eslintrc / .prettier # конвенции стиля
├─ config/               # конфиги по окружениям
├─ lib/                  # утилиты, чистые функции, без I/O
├─ api/                  # обработчики HTTP/WS-запросов
├─ domain/               # бизнес-логика, сущности
├─ db/                   # схемы, миграции, репозитории
├─ application/          # composition root, DI
└─ test/                 # юнит и интеграционные тесты

Принципы

  • Один слой — одна задача: api не знает про SQL, domain не знает про HTTP
  • Зависимости направлены внутрь: api → domain → db (но не наоборот)
  • Конфиг — не константы в коде: всё что меняется между окружениями вынести в config/ или env
  • Декларативный роутинг: список endpoint-ов в одном месте, не разбросан по if/else в обработчике
  • Точка входа маленькая: index.js собирает приложение из слоёв и стартует, не более

Пример композиции

// application/index.js
const config = require('../config');
const logger = require('../lib/logger')(config.log);
const db = require('../db')(config.db);
const domain = require('../domain')({ db, logger });
const api = require('../api')({ domain, logger });

api.listen(config.port);

Подводные камни

  • Преждевременная декомпозиция — для 100-строчного скрипта слои избыточны
  • Смешение бизнес-логики в обработчикахreq.body.amount > 0 уже бизнес-правило, не api
  • Кольцевые зависимости — domain тянет api → require цикл, Node вернёт половинчатый exports
  • Object.assign(exports, require('./a'), require('./b')) — антипаттерн, перекрытие ключей молча

🎓 Источники

  • 🎓 [Модули, слои, структура проекта, песочницы] · 2018-10-02 · YouTube
    • Тезисы: package.json и точка входа, папка lib, перекрытие имён при сборке через Object.assign, плохой пример смешения слоёв, уроки — какие слои нужны, конфигурация вместо констант, декларативный роутинг, справочник сериализаторов по типам, композиция наглядна и быстра
    • Цитата: «Без фреймворка — быстро и надёжно, если правильно разделить слои»
  • 🎓 [Слои, связанность и связность кода] · 2018-10-23 · YouTube
    • Тезисы: coupling vs cohesion, как мерить, как уменьшать связанность
  • 🎓 [Архитектурные принципы из курса Node.js 2024] · 2023-12-06 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2023-12-06 — ⭐ Архитектурные принципы из курса по Node.js 2024 и технологического стека Metar (GXSmwQ6j5RU).md)
    • Тезисы: композиция приложения из слоёв, namespace-based DI, application как composition root

См. также