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 позволит масштабировать проект без превращения его в монолит, а при необходимости — легко выделить любой модуль в отдельный сервис.

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

    Комментарии

    0

    Постройте личный план изучения React state менеджер Zustand до уровня Middle — бесплатно!

    React state менеджер Zustand — часть карты развития Frontend

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

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

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

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

    Vue 3 и Pinia

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

    Next.js - с нуля

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

    Feature-Sliced Design

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

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

    Картинка поста Паттерны проектирования на TypeScript: SOLID с примерами
    Иконка аватараАнтон
    Иконка календаря13 июня 2026
    typescriptsolidпаттерны проектирования+ 2middleИконка уровня middle

    Паттерны проектирования на TypeScript: SOLID с примерами

    Паттерны проектирования на TypeScript и принципы SOLID: разбор каждого принципа с практическими примерами кода, типичными ошибками и рекомендациями.

    Иконка чипа0
    Иконка глаза355
    Иконка комментариев0
    Картинка поста SOLID принципы в JavaScript: разбор на примерах
    Иконка аватараАнтон
    Иконка календаря26 июня 2026
    JavaScriptSOLIDООП+ 3middleИконка уровня middle

    SOLID принципы в JavaScript: разбор на примерах

    SOLID принципы в JavaScript — разбираем каждый принцип на реальных примерах кода, чтобы писать чистую и расширяемую архитектуру.

    Иконка чипа0
    Иконка глаза31
    Иконка комментариев0
    Картинка поста Замыкания в JavaScript: полное руководство с примерами
    Иконка аватараАнтон
    Иконка календаря25 июня 2026
    JavaScriptзамыканияосновы JS+ 2juniorИконка уровня junior

    Замыкания в JavaScript: полное руководство с примерами

    Замыкания в JavaScript — разбираем простыми словами: что захватывает функция, как работает scope chain и где применять на практике.

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