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 тренажёр
    • Проекты
    Главная
    Сообщество
    NestJS: как организовать модули, чтобы проект не стал монолитом

    NestJS: как организовать модули, чтобы проект не стал монолитом

    Аватар автора NestJS: как организовать модули, чтобы проект не стал монолитом

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

    Иконка календаря28 марта 2026
    nestjstypescriptarchitecturemiddleИконка уровня middle
    Картинка поста NestJS: как организовать модули, чтобы проект не стал монолитом

    Введение

    Организация модулей в NestJS — это фундамент, на котором строится масштабируемость всего проекта. Когда модульная архитектура NestJS выстроена правильно, добавление новых функций не превращается в болезненную переработку половины кодовой базы. Но если структуру проекта NestJS запустить с самого начала, через полгода вы получите монолит, где все зависит от всего.

    В этой статье разберем, как разделить модули в NestJS по доменным областям, какие типы модулей существуют и как избежать типичных ошибок при проектировании архитектуры.

    Почему модульная архитектура NestJS так важна

    NestJS изначально спроектирован вокруг концепции модулей. Декоратор @Module() — это основной строительный блок приложения. Он группирует контроллеры, сервисы и провайдеры в логически связанные единицы.

    Проблема в том, что многие разработчики создают модули формально: один модуль на одну сущность базы данных. В итоге UserModule импортирует OrderModule, который импортирует PaymentModule, а тот снова зависит от UserModule. Появляются циклические зависимости, и проект теряет модульность.

    // Плохо: модуль привязан к сущности, а не к домену
    @Module({
      imports: [TypeOrmModule.forFeature([User])],
      controllers: [UserController],
      providers: [UserService],
      exports: [UserService], // экспортируем всё подряд
    })
    export class UserModule {}
    

    Разделение модулей NestJS по доменным областям

    Вместо создания модуля на каждую сущность, группируйте код по бизнес-доменам. Домен — это область бизнес-логики, которая может существовать относительно независимо.

    Например, вместо отдельных UserModule, ProfileModule и AvatarModule создайте один AccountModule, объединяющий всё, что связано с учетной записью пользователя:

    // Хорошо: модуль организован по домену
    @Module({
      imports: [TypeOrmModule.forFeature([User, Profile, Avatar])],
      controllers: [AccountController, ProfileController],
      providers: [
        AccountService,
        ProfileService,
        AvatarService,
      ],
      exports: [AccountService], // экспортируем только публичный API
    })
    export class AccountModule {}
    

    Такой подход дает три преимущества. Во-первых, уменьшается количество модулей и связей между ними. Во-вторых, весь связанный код находится рядом. В-третьих, модуль можно легко вынести в отдельный микросервис.

    Как правильно использовать exports и imports в модулях

    Главное правило: экспортируйте из модуля минимум. Каждый экспортированный провайдер — это публичный контракт, который потом сложно изменить.

    @Module({
      imports: [AccountModule, ProductModule],
      controllers: [OrderController],
      providers: [
        OrderService,
        OrderRepository,
        PriceCalculator, // внутренний провайдер, не экспортируем
      ],
      exports: [OrderService], // только фасад для других модулей
    })
    export class OrderModule {}
    

    Если другому модулю нужна функциональность, он должен работать через экспортированный сервис, а не напрямую обращаться к внутренним провайдерам. Это и есть инкапсуляция на уровне модулей.

    Shared-модули для общей функциональности

    Некоторые провайдеры нужны повсюду: логирование, конфигурация, работа с кешем. Для них создается shared-модуль:

    @Module({
      providers: [LoggerService, CacheService, ConfigService],
      exports: [LoggerService, CacheService, ConfigService],
    })
    export class SharedModule {}
    

    Если модуль используется почти везде, можно сделать его глобальным через декоратор @Global():

    @Global()
    @Module({
      providers: [ConfigService],
      exports: [ConfigService],
    })
    export class ConfigModule {}
    

    Но не злоупотребляйте @Global(). Глобальные модули нарушают явность зависимостей — непонятно, откуда берется провайдер. Используйте глобальные модули только для инфраструктурных вещей.

    Динамические модули для гибкой конфигурации

    Динамические модули позволяют настраивать поведение при импорте. Это удобно для библиотечных модулей, которые должны работать с разными параметрами:

    @Module({})
    export class DatabaseModule {
      static forRoot(options: DatabaseOptions): DynamicModule {
        return {
          module: DatabaseModule,
          providers: [
            {
              provide: 'DATABASE_OPTIONS',
              useValue: options,
            },
            DatabaseService,
          ],
          exports: [DatabaseService],
          global: true,
        };
      }
    
      static forFeature(entities: Type[]): DynamicModule {
        return {
          module: DatabaseModule,
          providers: entities.map((entity) => ({
            provide: getRepositoryToken(entity),
            useFactory: (db: DatabaseService) => db.getRepository(entity),
            inject: [DatabaseService],
          })),
          exports: entities.map((entity) => getRepositoryToken(entity)),
        };
      }
    }
    

    Паттерн forRoot / forFeature позволяет разделить инициализацию (один раз в AppModule) и использование (в каждом фича-модуле).

    Структура проекта NestJS для масштабируемости

    Рекомендуемая структура директорий для среднего и крупного проекта:

    src/
      modules/
        account/
          account.module.ts
          account.controller.ts
          account.service.ts
          dto/
          entities/
        order/
          order.module.ts
          order.controller.ts
          order.service.ts
          dto/
          entities/
        payment/
          payment.module.ts
          payment.controller.ts
          payment.service.ts
      shared/
        shared.module.ts
        logger/
        cache/
      config/
        config.module.ts
      app.module.ts
      main.ts
    

    Каждый доменный модуль содержит всё необходимое: контроллеры, сервисы, DTO и сущности. Это позволяет при необходимости вынести модуль целиком в отдельный микросервис.

    Частые ошибки при организации модулей

    Модуль на каждую сущность. Создание UserModule, UserProfileModule, UserSettingsModule по отдельности приводит к десяткам мелких модулей с перекрестными зависимостями. Объединяйте связанные сущности в один доменный модуль.

    Циклические зависимости. Если ModuleA импортирует ModuleB, а ModuleB импортирует ModuleA, используйте forwardRef(). Но лучше пересмотреть архитектуру — циклические зависимости сигнализируют о неправильном разделении ответственности.

    // Костыль, а не решение
    @Module({
      imports: [forwardRef(() => OrderModule)],
    })
    export class UserModule {}
    

    Экспорт всех провайдеров. Когда модуль экспортирует все свои сервисы, репозитории и хелперы, он теряет инкапсуляцию. Экспортируйте только фасадный сервис.

    God Module. Один модуль, который импортирует и экспортирует всё подряд. Обычно это AppModule, который разрастается до сотен строк. Держите AppModule чистым — он только импортирует доменные модули.

    Заключение

    Правильная организация модулей в NestJS — это не про количество файлов и папок, а про четкие границы между доменами. Разделяйте код по бизнес-областям, экспортируйте минимум, используйте shared-модули для инфраструктуры и динамические модули для гибкой конфигурации. Такая модульная архитектура NestJS позволит масштабировать проект без превращения его в монолит, а при необходимости — легко выделить любой модуль в отдельный сервис.

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

    Комментарии

    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 ₽
    Подробнее

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

    Картинка поста Clean Architecture: чистая архитектура для веб-разработки
    Иконка аватараАнтон
    Иконка календаря02 июля 2026
    архитектураclean architecturetypescript+ 3middleИконка уровня middle

    Clean Architecture: чистая архитектура для веб-разработки

    Clean Architecture разделяет код на независимые слои: бизнес-логика не зависит от фреймворков, БД и внешних сервисов. Разбираем на примерах TypeScript.

    Иконка чипа0
    Иконка глаза707
    Иконка комментариев0
    Картинка поста Регулярные выражения в JavaScript: полное руководство
    Иконка аватараАнтон
    Иконка календаря06 августа 2026
    JavaScriptрегулярные выраженияregexp+ 2middleИконка уровня middle

    Регулярные выражения в JavaScript: полное руководство

    Регулярные выражения в JavaScript позволяют искать, проверять и заменять текст по шаблону. Разбираем синтаксис, методы и частые ошибки с примерами.

    Иконка чипа0
    Иконка глаза144
    Иконка комментариев0
    Картинка поста React useState и useEffect: полное руководство 2024
    Иконка аватараАнтон
    Иконка календаря29 июля 2026
    ReactuseStateuseEffect+ 3juniorИконка уровня junior

    React useState и useEffect: полное руководство 2024

    Разбираем useState и useEffect с нуля: как управлять состоянием и побочными эффектами в React-компонентах с реальными примерами.

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