PurpleSchool — курсы программирования онлайн
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развития
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Архитектура Node.js: слои, модули и Dependency Injection

    Архитектура Node.js: слои, модули и Dependency Injection

    Аватар автора Архитектура Node.js: слои, модули и Dependency Injection

    Антон Ларичев

    Иконка календаря08 июня 2026
    Node.jsАрхитектураBackendDependency InjectionmiddleИконка уровня middle
    Картинка поста Архитектура Node.js: слои, модули и Dependency Injection

    Введение

    Когда Node.js проект вырастает из учебного «hello world» в реальный продукт, привычный подход «всё в одном файле» начинает мешать. Контроллеры обращаются к базе напрямую, бизнес-логика переплетается с HTTP-слоем, а тесты превращаются в боль. Решение — продуманная архитектура: разделение на слои, выделение модулей и внедрение зависимостей (Dependency Injection, DI).

    В этой статье разберём, как организовать Node.js приложение так, чтобы оно оставалось гибким при росте кодовой базы, легко тестировалось и позволяло безболезненно менять реализации компонентов.

    Слоистая архитектура

    Классический подход — разделить приложение на три-четыре слоя, где каждый отвечает за свою зону ответственности:

    • Controller (Presentation) — принимает HTTP-запросы, валидирует входные данные, формирует ответ.
    • Service (Business Logic) — содержит бизнес-правила, не знает про HTTP и базу данных.
    • Repository (Data Access) — работает с источником данных: ORM, SQL, внешние API.
    • Domain (Entity) — модели предметной области и инварианты.

    Главное правило: зависимости направлены строго вниз. Контроллер знает о сервисе, сервис — о репозитории. Обратные связи запрещены.

    // controllers/user.controller.js
    export class UserController {
      constructor(userService) {
        // зависимость передана извне
        this.userService = userService;
      }
    
      async getById(req, res) {
        const user = await this.userService.findById(req.params.id);
        if (!user) return res.status(404).json({ error: 'Not found' });
        res.json(user);
      }
    }
    
    // services/user.service.js
    export class UserService {
      constructor(userRepository) {
        this.userRepository = userRepository;
      }
    
      async findById(id) {
        // здесь только бизнес-логика, без HTTP и SQL
        return this.userRepository.findById(id);
      }
    }
    

    Модули и границы

    Слои описывают «горизонтальное» деление. Но при росте проекта важнее «вертикальное» — по доменам или фичам. Это называется модульной (feature-based) структурой.

    src/
      users/
        user.controller.js
        user.service.js
        user.repository.js
        user.model.js
      orders/
        order.controller.js
        order.service.js
        order.repository.js
      shared/
        logger.js
        db.js
    

    Каждый модуль самодостаточен: имеет собственный слой данных, бизнес-логики и контроллеров. Общение между модулями идёт через явные публичные интерфейсы, а не через прямой импорт внутренних файлов соседа.

    Dependency Injection: зачем и как

    Dependency Injection — это паттерн, при котором объект получает свои зависимости извне, а не создаёт их сам. Без DI код выглядит так:

    // плохо: жёсткая связь
    import { db } from '../db.js';
    
    export class UserService {
      async findById(id) {
        return db.query('SELECT * FROM users WHERE id = $1', [id]);
      }
    }
    

    Здесь сервис намертво привязан к конкретному инстансу базы. Замокать его в тестах сложно, заменить Postgres на MongoDB — почти нереально.

    С DI зависимости передаются через конструктор:

    // хорошо: зависимость инжектируется
    export class UserService {
      constructor(userRepository) {
        this.userRepository = userRepository;
      }
    
      async findById(id) {
        return this.userRepository.findById(id);
      }
    }
    

    Теперь сервис не знает, откуда берутся данные — это решает тот, кто его собирает.

    Простейший DI-контейнер

    Для небольшого проекта достаточно ручной сборки в одной точке (composition root):

    // container.js
    import { Pool } from 'pg';
    import { UserRepository } from './users/user.repository.js';
    import { UserService } from './users/user.service.js';
    import { UserController } from './users/user.controller.js';
    
    const pool = new Pool({ connectionString: process.env.DATABASE_URL });
    
    const userRepository = new UserRepository(pool);
    const userService = new UserService(userRepository);
    const userController = new UserController(userService);
    
    export { userController };
    

    Для более крупных приложений подойдут библиотеки вроде awilix, tsyringe или встроенный DI в NestJS.

    Тестирование с DI

    DI делает юнит-тесты тривиальными — достаточно передать заглушку:

    import { test } from 'node:test';
    import assert from 'node:assert';
    import { UserService } from './user.service.js';
    
    test('возвращает пользователя по id', async () => {
      // мок-репозиторий вместо настоящего
      const fakeRepo = {
        findById: async (id) => ({ id, name: 'Anna' }),
      };
      const service = new UserService(fakeRepo);
    
      const user = await service.findById(1);
      assert.strictEqual(user.name, 'Anna');
    });
    

    Частые ошибки

    • Сквозные импорты слоёв. Контроллер тянет репозиторий напрямую — слоистость нарушена, бизнес-логика размазывается по проекту.
    • «Анемичные» сервисы. Сервис превращается в тонкую прокладку, которая просто вызывает репозиторий — это знак, что логику стоит вынести в доменные модели.
    • Глобальные синглтоны. Импорт готового инстанса базы или логгера из модуля убивает тестируемость и плодит скрытые зависимости.
    • Слишком ранняя абстракция. Не стоит писать пять интерфейсов «на вырост» для маленького проекта — DI вводится тогда, когда появляется реальная боль с тестами или заменой реализации.
    • DI-контейнер как сервис-локатор. Если компоненты сами достают зависимости из контейнера, это уже не DI, а антипаттерн Service Locator.

    Заключение

    Грамотная архитектура Node.js приложения строится на трёх китах: разделении на слои с однонаправленными зависимостями, модульной организации по доменам и внедрении зависимостей через конструкторы. Такой подход даёт предсказуемую структуру, простое тестирование и возможность менять реализации без переписывания половины кода.

    Начните с малого: вынесите бизнес-логику из контроллеров в сервисы, отделите работу с данными в репозитории и передавайте зависимости явно. Когда проект подрастёт — добавьте DI-контейнер. Архитектура должна служить задаче, а не наоборот.

    Иконка глаза8

    Комментарии

    0

    Постройте личный план изучения TypeScript с нуля - полный курс и паттерны проектирования до уровня Middle — бесплатно!

    TypeScript с нуля - полный курс и паттерны проектирования — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    React и Redux Toolkit

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    React Native и Expo Router

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.7
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Основы Swift и iOS

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.7
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста REST API на Node.js и Express с нуля: пошаговое руководство
    Иконка аватараАнтон
    Иконка календаря02 июня 2026
    Node.jsExpressREST API+ 1juniorИконка уровня junior

    REST API на Node.js и Express с нуля: пошаговое руководство

    REST API на Node.js и Express: пошаговое руководство для начинающих. Создаём сервер, маршруты, middleware и обрабатываем запросы.

    Иконка чипа0
    Иконка глаза168
    Иконка комментариев0
    Картинка поста JWT аутентификация в Node.js: access и refresh токены
    Иконка аватараАнтон
    Иконка календаря27 мая 2026
    Node.jsJWTАутентификация+ 2middleИконка уровня middle

    JWT аутентификация в Node.js: access и refresh токены

    JWT аутентификация в Node.js: разбираем работу access и refresh токенов, генерацию, валидацию и безопасное хранение на практике.

    Иконка чипа0
    Иконка глаза221
    Иконка комментариев0
    Картинка поста WebSocket на Node.js: строим real-time чат с нуля за час
    Иконка аватараАнтон
    Иконка календаря26 мая 2026
    Node.jsWebSocketJavaScript+ 2middleИконка уровня middle

    WebSocket на Node.js: строим real-time чат с нуля за час

    WebSocket на Node.js: пошаговое руководство по созданию real-time чата с библиотекой ws, обработкой подключений и broadcast-сообщений.

    Иконка чипа0
    Иконка глаза248
    Иконка комментариев0
    Иконка чипа0