PurpleSchool — курсы программирования онлайн
  • Пути
    • 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Иконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • 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-контейнер. Архитектура должна служить задаче, а не наоборот.

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

    Комментарии

    0

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

    Neovim - практика и настройка — часть карты развития Frontend, Backend, DevOps

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

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

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

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

    React и Redux Toolkit

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

    Zustand

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

    Next.js - с нуля

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

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

    Иконка аватараАнтон
    Иконка календаря27 сентября 2026
    openaiAI-инструментыDevOps+ 2middleИконка уровня middle

    Как установить Codex на Windows в России

    Установка Codex на Windows состоит из двух разных дел, и второе обычно застаёт врасплох. Сам агент ставится одной командой `npm install -g @openai/codex` и на Windows работает так же, как на macOS: пакет скачивается из npm без всяких препятствий. А вот первый же запрос к модели закончится отказом: по умолчанию Codex авторизуется через аккаунт ChatGPT, а этот путь из России закрыт — ни вход, ни оплата подписки российской картой не пройдут. Поэтому после установки нужен второй шаг: прописать Codex другой адрес модельного провайдера и ключ к нему. Ключ можно взять в [AI для кода от PurpleSchool](

    Иконка чипа0
    Иконка глаза396
    Иконка комментариев0
    Картинка поста Сколько стоит Cursor в 2026 и как оплатить из России
    Иконка аватараАнтон
    Иконка календаря17 октября 2026
    AI-инструментыAnthropicapijuniorИконка уровня junior

    Сколько стоит Cursor в 2026 и как оплатить из России

    С 12 октября 2026 года Cursor продаётся в составе The X Subscription — общей подписки на Grok, Cursor и X Premium. Тарифы такие: Free — бесплатно, Plus — $8 в месяц, Premium — $30, Super — $100, Ultra — $200. Модели Claude и GPT в Cursor доступны только с Premium и оплачиваются отдельно, по ценам API. Из России оплатить подписку российской картой нельзя, а модели Claude и GPT Cursor не отдаёт в регионах, которые блокируют их провайдеры. Рабочая схема для России — бесплатный Cursor как редактор и агент Claude Code внутри него с ключом AI для кода от PurpleSchool от 499 ₽ в месяц.

    Иконка чипа0
    Иконка глаза21
    Иконка комментариев0
    Картинка поста Claude без VPN в России: как пользоваться в 2026
    Иконка аватараАнтон
    Иконка календаря16 октября 2026
    AnthropicAI-инструментыClaude CodejuniorИконка уровня junior

    Claude без VPN в России: как пользоваться в 2026

    Сайт claude.ai и приложения Claude из России без VPN не работают: Anthropic не включает Россию в список поддерживаемых регионов и закрывает доступ и к чату, и к API. Без VPN Claude доступен одним способом — через API-посредника, который сам обращается к Anthropic из поддерживаемой страны, а вам выдаёт ключ с оплатой в рублях. Так работает AI для кода от PurpleSchool: ключ подключается к Claude Code, VS Code, Cursor и другим инструментам разработчика, а также к вашему собственному коду. Окно чата как на claude.ai такой способ не даёт — это доступ к модели, а не к сайту.

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