Структура 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