О себе

Всем привет, меня зовут Ильшат.
Я пишу бэкенд на Typescript и NestJS уже 7 лет.

Помимо бэкэнда я пишу ещё и фронтенд на Angular, благодаря схожести NestJS и Angular я и выбрал его в качестве основного фреймворка для разработки бэкенда, соответственно Typescript стал моим основным языком программирования.

Проблемы

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

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

Помимо написания кода продукта, также есть необходимость в унифицированном процессе сборки и доставки этого кода непосредственно заказчику.

Когда продукт и команда одна, то нет проблем с разной кодовой базой, деплоем и доставкой, один раз настроил и работает как часы.

Но когда начинают появляться новые продукты или новые микросервисы в отдельных репозиториях, все настройки и код начинают разъезжаться.

Если в организации есть девопс специалист или целый отдел по автоматизации процесcов разработки, они могут работу по синхронизации настроек девопс взять на себя, но проблема синхронизации архитектур продуктов и архитектур модулей останется.

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

Решения

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

Постоянная настройка девопс под проекты с различными модулями также очень сильно напрягала.

Коллекция утилит NestJS-mod предназначена для унификации приложений и модулей, а также представляет новые логические возможности разделения обязанностей между модулями (System, Core, Feature, Integration, Infrastructure).

Поскольку все части приложения унифицированы, вы можете создать отчёт по всей инфраструктуре проекта.

Единым источником информаци�� для работы, разворачивания, доставки и документирования служит само NestJS-mod приложение.

NestJS-mod модули сами генерируют все необходимые настройки и скрипты запуска различных инфраструктурных систем.

Быстрый старт

# Создать пустой nx проект
npx --yes create-nx-workspace@19.5.3 --name=project-name --preset=apps --interactive=false --ci=skip

# Перейти в созданную папку
cd project-name

# Установить схематик для генерации NestJS-mod приложения
npm install --save-dev @nestjs-mod/schematics@latest

# Создать приложение NestJS-mod
./node_modules/.bin/nx g @nestjs-mod/schematics:application --directory=apps/app-name --name=app-name --projectNameAndRootFormat=as-provided --strict=true

# Подготовить все файлы
npm run manual:prepare

# Запустить приложения в watch-режиме
npm run serve:dev:app-name

Типы модулей

При создании модуля NestJS все существующие модули импортируются в одну опцию imports главного модуля приложения.

Когда все модули идут единым списком, то мы не можем сразу определить какой модуль является бизнес модулем, а какой несет общую логику по логированию.

NestJS-mod имеет не одну общую опцию для импорта модулей а несколько.

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

import { bootstrapNestApplication } from "@nestjs-mod/common";

bootstrapNestApplication({
  modules: {
    system: [],
    core: [],
    feature: [],
    integrations: [],
    infrastructure: [],
  },
});

Порядок обработки модулей: 1) system, 2) core, 3) feature, 4) integrations, 5) infrastructure

Системные модули (System)

Модули для работы всего приложения.

Примеры: запуск приложения NestJS, запуск микросервисов и т. д.
Совместимы только с NestJS-mod.

Модули ядра (Core)

Модули с типом “Ядро” нужны для работы функциональных модулей и модулей интеграции.

Примеры: основной модуль с подключением к базе данных, основной модуль для подключения к aws и т. д.
Совместимы с NestJS и NestJS-mod.

Функциональные модули (Feature)

Функциональные (“фича”) модули с бизнес-логикой приложения.

Совместимы с NestJS и NestJS-mod.

Модули интеграции (Integration)

“Интеграционные” модули для организации связи между разными функциональными, системными или модулями ядра.

Пример: после создания пользователя в модуле UsersModule у которого тип модуля “Feature” вам необходимо отправить ему письмо из модуля NotificationsModule с типом “Core”, при этом информацию о транспорте и способе отправки модуль юзеров не имеет как и явную связь с модулем нотификации, модуль юзеров имеет просто метод в конфигурации afterCreateUser реализация которого уже будет передана из некоего модуля интеграции.
Совместимы с NestJS и NestJS-mod.

Модули инфраструктуры (Infrastructure)

Модули для создания конфигурационных файлов различных внешних от приложения сервисов. Данные модули исключаются из итогового списка модулей которые будут загружены в NestJS.

Примеры: файл docker-compose для поднятия базы данных, конфигурационный файл gitlab для развертывания приложения.
Совместимы только с NestJS-mod.

Утилиты

Конфигурация (Config model)

Декораторы ConfigModel, ConfigModelProperty для описания доступных настроек модуля и функция configTransform для его сериализации и проверки. Значения должны быть описаны в коде.

Пример обычного NestJS приложения с конфигурацией

import {
  ConfigModel,
  ConfigModelProperty,
  configTransform,
} from "@nestjs-mod/common";
import { DynamicModule, Module } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { IsNotEmpty } from "class-validator";

// Описываем класс конфигурации
@ConfigModel()
class AppConfig {
  @ConfigModelProperty()
  @IsNotEmpty()
  option!: string;
}

// Описываем модуль который получает значения конфигурации при вызове метода forRoot
@Module({ providers: [AppConfig] })
class AppModule {
  static forRoot(config: Partial<AppConfig>): DynamicModule {
    return {
      module: AppModule,
      providers: [
        {
          provide: `${AppConfig.name}_loader`,
          useFactory: async (emptyAppConfig: AppConfig) => {
            if (config.constructor !== Object) {
              Object.setPrototypeOf(emptyAppConfig, config);
            }
            const obj = await configTransform({
              model: AppConfig,
              data: config,
            });
            Object.assign(emptyAppConfig, obj.data);
          },
          inject: [AppConfig],
        },
      ],
    };
  }
}

// Пробуем запустить приложение и при этом ничего не передаем в модуль
async function bootstrap1() {
  const app = await NestFactory.create(AppModule.forRoot({}));
  await app.listen(3000);
}

// Получаем ошибку валидации
// throw new ConfigModelValidationErrors(validateErrors);
// isNotEmpty: option should not be empty
bootstrap1();

// Пробуем запустить приложение и при этом передаем в модуль значения для конфигурации
async function bootstrap2() {
  const app = await NestFactory.create(AppModule.forRoot({ option: "value1" }));
  console.log(app.get(AppConfig)); // output: { option: 'value1' }
  await app.listen(3000);
}

// Ошибки нет
bootstrap2();

Переменные окружения (Env model)

Декораторы EnvModel, EnvModelProperty для описания переменных окружения модуля и функция envTransform для его сериализации и проверки. Значения могут быть автоматически прочитаны из process.env или других источников таких как https://www.vaultproject.io или https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv.

Пример NestJS приложения с различными способами использования переменных окружения

import { EnvModel, EnvModelProperty, envTransform } from "@nestjs-mod/common";
import { DynamicModule, Module } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { IsNotEmpty } from "class-validator";

// Описываем класс для работы с переменными окружения
@EnvModel()
class AppEnv {
  @EnvModelProperty()
  @IsNotEmpty()
  option!: string;
}

// Описываем модуль который получает значения переменных окружения при вызове метода forRoot
@Module({ providers: [AppEnv] })
class AppModule {
  static forRoot(env: Partial<AppEnv>): DynamicModule {
    return {
      module: AppModule,
      providers: [
        {
          provide: `${AppEnv.name}_loader`,
          useFactory: async (emptyAppEnv: AppEnv) => {
            if (env.constructor !== Object) {
              Object.setPrototypeOf(emptyAppEnv, env);
            }
            const obj = await envTransform({
              model: AppEnv,
              data: env,
            });
            Object.assign(emptyAppEnv, obj.data);
          },
          inject: [AppEnv],
        },
      ],
    };
  }
}

// Пробуем запустить приложение и при этом ничего не передаем в модуль и не имеем нужных переменных в process.env
async function bootstrap1() {
  const app = await NestFactory.create(AppModule.forRoot({}));
  await app.listen(3000);
}

// Получаем ошибку валидации
// throw new ConfigModelValidationErrors(validateErrors);
// isNotEmpty: option should not be empty
bootstrap1();

// Пробуем запустить приложение и при этом передаем вручную в модуль значения переменных окружения, process.env по прежнему пустой
async function bootstrap2() {
  const app = await NestFactory.create(AppModule.forRoot({ option: "value1" }));
  console.log(app.get(AppEnv)); // output: { option: 'value1' }
  await app.listen(3000);
}

// Ошибки нет
bootstrap2();

// Пробуем запустить приложение и при этом не передаем в модуль значения переменных окружения, а ложим их в process.env
async function bootstrap3() {
  process.env["OPTION"] = "value1";
  const app = await NestFactory.create(AppModule.forRoot({}));
  console.log(app.get(AppEnv)); // output: { option: 'value1' }
  await app.listen(3000);
}

// Ошибки нет
bootstrap3();

Названия ключей полей формируются с помощью форматтеров (formatters).

Примеры:

  • основной форматтер - трансформирует цепочку из наименований: приложения, контекста, модуля, свойства, которые формируется в процессе запуска создания приложения и модуля (пример: <PROJECTNAME><CONTEXTNAME><MODULENAME><PROPERTY_NAME>). (код)

  • кастомный форматтер - наследуется от основного трансформера и включает в цепочку наименований дополнительную статическую строку <PROJECTNAME><CONTEXTNAME><MODULENAME>_STATIC_STRING<PROPERTY_NAME>) (код)

Значения переменных окружения получаются при помощи экстракторов (extractors), которые в своей работе используют названия ключей которые создали форматтеры.

Примеры:

  • основной экстрактор - получает значение по ключу из обьекта который передали в функицю configTransform (код)

  • process.env экстрактор - получает значения из окружения текущего процесса (код)

Функция создания NestJS-mod модуля (createNestModule)

Функция createNestModule для создания динамического NestJS модуля с возможностью настройки через конфигурации или переменные окружения, а также предоставляющая возможность использовать часть сервисов модуля через метод forFeature или передавать часть конфигураций из фича модулей.

Все модули имеют возможность создания нескольких параллельных именованных экземпляров модуля с различными входными параметрами, для этого нужно передать название экземпляра в опцию contextName.

В отличии от NestJS модулей, NestJS-mod модули могут содержать дополнительные методы обертки которые будут вызваны при построении приложения NestJS.

Пример создания нативного NestJS модуля с помощью функции createNestModule и передача различных видов конфигураций

import {
  ConfigModel,
  ConfigModelProperty,
  EnvModel,
  EnvModelProperty,
  createNestModule,
  getNestModuleDecorators,
  InjectableFeatureConfigurationType,
} from "@nestjs-mod/common";
import { Injectable } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { IsNotEmpty } from "class-validator";

// App1Module

const { InjectFeatures } = getNestModuleDecorators({
  moduleName: "App1Module",
});

@ConfigModel()
class AppFeatureConfig {
  @ConfigModelProperty()
  @IsNotEmpty()
  featureOptionConfig!: string;
}

@Injectable()
class AppFeaturesService {
  constructor(
    @InjectFeatures()
    private readonly appFeatureConfigs: InjectableFeatureConfigurationType<AppFeatureConfig>[]
  ) {}

  getFeatureConfigs() {
    return this.appFeatureConfigs.map(
      ({ featureConfiguration }) => featureConfiguration
    );
  }
}

const { App1Module } = createNestModule({
  moduleName: "App1Module",
  sharedProviders: [AppFeaturesService],
  featureConfigurationModel: AppFeatureConfig,
});

@ConfigModel()
class App2Config {
  @ConfigModelProperty()
  @IsNotEmpty()
  option!: string;
}

@Injectable()
class App2Service {
  constructor(
    private readonly appFeaturesService: AppFeaturesService,
    private readonly app2Config: App2Config
  ) {}

  getFeatureConfigs() {
    return this.appFeaturesService.getFeatureConfigs();
  }

  getConfig() {
    return this.app2Config;
  }
}

// App2Module

const { App2Module } = createNestModule({
  moduleName: "App2Module",
  imports: [
    App1Module.forFeature({
      featureModuleName: "App2Module",
      featureConfiguration: { featureOptionConfig: "featureOptionConfig-app2" },
    }),
  ],
  providers: [App2Service],
  configurationModel: App2Config,
});

@EnvModel()
class App3Env {
  @EnvModelProperty()
  @IsNotEmpty()
  option!: string;
}

@Injectable()
class App3Service {
  constructor(
    private readonly appFeaturesService: AppFeaturesService,
    private readonly app3Env: App3Env
  ) {}

  getFeatureConfigs() {
    return this.appFeaturesService.getFeatureConfigs();
  }

  getEnv() {
    return this.app3Env;
  }
}

const { App3Module } = createNestModule({
  moduleName: "App3Module",
  imports: [
    App1Module.forFeature({
      featureModuleName: "App2Module",
      featureConfiguration: { featureOptionConfig: "featureOptionConfig-app3" },
    }),
  ],
  providers: [App3Service],
  environmentsModel: App3Env,
});

// Test

const { AppModule } = createNestModule({
  moduleName: "AppModule",
  imports: [
    App1Module.forRoot(),
    App2Module.forRoot({ configuration: { option: "appConfig3value" } }),
    App3Module.forRoot({ environments: { option: "appEnv2value" } }),
  ],
});

async function bootstrap() {
  const app = await NestFactory.create(AppModule.forRoot());
  const appFeatureScannerService = app.get(AppFeaturesService);
  const app2Service = app.get(App2Service);
  const app3Service = app.get(App3Service);

  console.log(appFeatureScannerService.getFeatureConfigs()); // output: [{ featureOptionConfig: 'featureOptionConfig-app2' }, { featureOptionConfig: 'featureOptionConfig-app3' }]
  console.log(app2Service.getFeatureConfigs()); // output: [{ featureOptionConfig: 'featureOptionConfig-app2' }, { featureOptionConfig: 'featureOptionConfig-app3' }]
  console.log(app3Service.getFeatureConfigs()); // output: [{ featureOptionConfig: 'featureOptionConfig-app2' }, { featureOptionConfig: 'featureOptionConfig-app3' }]
  console.log(app2Service.getConfig()); // output: { option: 'appConfig3value' }
  console.log(app3Service.getEnv()); // output: { option: 'appEnv2value' }
}

bootstrap();

Методы обертки

Эти методы вызывает функция bootstrapNestApplication при построении приложения NestJS-mod, они не будут вызываться, если модуль подключить в обычное NestJS приложение.

  • preWrapApplication - вызывается у всех корневых модулей, тут мы можем создать ещё один дополнительный корневой модуль, конфигурация для которого будет динамически сформированна на основе оригинального модуля, например: установливаем новый префикс к названию ключей при работе с перемеными окружения (пример кода)

  • wrapApplication - метод который может создать NestJS приложение или микросервис, возвращает инстанс созданного приложения. (пример кода)

  • postWrapApplication - данный метод отрабатывает после создания приложения, например нужно запустить прослушку HTTP-порта (пример кода)

При создании модуля можно передать множество различных новых параметров, там есть как оригинальные опции NestJS так и расширенные.

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

Опции создания NestJS-mod модуля

environmentsModel

Класс и его свойства помечены декораторами типа “Переменные окружения” (Env model), в нем содержатся свойства с примитивными типами, используемые в модуле, значения которых можно получить из различных источников, таких как: process.env или consul-kv.

configurationModel

Класс и его свойства помечены декораторами типа “Конфигурация” (Config model), свойства примитивных и сложных типов, которые используются в модуле, значения для них необходимо передавать при подключении модуля к приложению, описываются в коде.

staticEnvironmentsModel

Класс “Переменных окружения” (Env model) со статическими свойствами примитивного типа, могут использоваться в момент генерации метаданных модуля, значения можно получить из различных источников, например: process.env или consul-kv.

Пример: различные условия импортов в зависимости от переменных окружения, динамические роуты для контроллеров REST.

staticConfigurationModel

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

featureEnvironmentsModel

Переменные окружения “фича” мод��лей с примитивными типами, значения которых можно получить из различных источников, таких как: process.env или consul-kv.

Пример: название переменной окружения для подключения к базе данных фича модуля отличается от названия переменной окружения рутового коннекшена.

featureConfigurationModel

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

Пример: транспорт для отправки сообщения можно определить как продуктовый “фича” функционал, но основная реализация обхода адресатов и отправка будет являться “ядром” или “интеграционным” модулем.

Создание основного динамического модуля и передача асинхронной конфигурации

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

Помимо асинхронной фабрики, можно также использовать класс и передачу по значению, все как и в обычном NestJS.

В NestJS-mod есть ещё один способ передачи конфигурации, это передача Observable потока с значениями этой конфигурации. Данный способ нужен когда значения могут меняться с течением времени.

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

Работа с опциями которые были переданы из других модулей (фича конфигурации)

Так как в момент инициализации NestJS модулей, порядок подгрузки (резолвинга) может отличатся, однозначно в момент старта мы можем получить все “фича конфигурации” только в NestJS хуке onApplicationBootstrap.

Для получения нужно использовать декоратор InjectFeatures.
В запущенном приложении (рантайм) проблем с доступом ко всем конфигурациям уже нет.

Декораторы для работы с сущностями модуля

Так-как использование декораторов это статичный код и его нельзя менять в реальном времени, декораторы для каждого модуля нужно создавать вручную через функцию getNestModuleDecorators

Типы декораторов:

InjectService

Для подключения провайдера по инжектируемому токену или классу.

Пример: если в приложении несколько инстансов одного и того же модуля, но с разным названием контекста, то в декоратор можно передать название этого контекста.

InjectFeatures

Для подключения массива со всеми конфигурациями полученным из различных модулей.

Пример: Есть “систем” модуль доступности сайта и есть фича конфигурация через которую модуль “ядра” для работы с БД сможет оповещать о работоспособности базы данных. На основе этого списка “систем” модуль принимает решение - можно ли пускать людей на бэкенд.

InjectAllFeatures

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

InjectFeatureEnvironments

Помимо конфигураций можно также получить все переменные окружения которые использовали фича модули.

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

InjectAllFeatureEnvironments

Тоже самое что и InjectFeatureEnvironments только собирает информацию по всем инстансам модуля.

InjectModuleSettings

Иногда нужно получить все метаданные классов конфигураций и классов для переменных окружения модуля, это декоратор для этого.

Пример: приложение собирает все значения конфигураций переменных окружения с названиями ключей и формирует некий отчёт .

InjectAllModuleSettings

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

Функция для создания NestJS-mod приложения

Это наверное самая простая функция bootstrapNestApplication, она просто пробегается по объекту с массивом модулей и вызывает враппер методы.

Пример приложения

import {
  DefaultNestApplicationInitializer,
  DefaultNestApplicationListener,
  EnvModel,
  EnvModelProperty,
  bootstrapNestApplication,
  createNestModule,
} from "@nestjs-mod/common";
import { Injectable, Logger } from "@nestjs/common";
import { IsNotEmpty } from "class-validator";

@EnvModel()
class AppEnv {
  @EnvModelProperty()
  @IsNotEmpty()
  option!: string;
}

@Injectable()
class AppService {
  constructor(private readonly appEnv: AppEnv) {}

  getEnv() {
    return this.appEnv;
  }
}

const { AppModule } = createNestModule({
  moduleName: "AppModule",
  environmentsModel: AppEnv,
  providers: [AppService],
});

process.env["OPTION"] = "value1";

const globalPrefix = "api";

bootstrapNestApplication({
  modules: {
    system: [
      DefaultNestApplicationInitializer.forRoot(),
      DefaultNestApplicationListener.forRoot({
        staticEnvironments: { port: 3000 },
        staticConfiguration: {
          preListen: async ({ app }) => {
            if (app) {
              const appService = app.get(AppService);
              console.log(appService.getEnv()); // output: { option: 'value1' }
              app.setGlobalPrefix(globalPrefix);
            }
          },
          postListen: async ({ current }) => {
            Logger.log(
              `? Application is running on: http://${
                current.staticEnvironments?.hostname ?? "localhost"
              }:${current.staticEnvironments?.port}/${globalPrefix}`
            );
          },
        },
      }),
    ],
    feature: [AppModule.forRoot()],
  },
});

Схематики

Целевой тип приложений для использования NestJS-mod это монорепозиторий на https://nx.dev.

Так-как шаблон NestJS-mod немного отличается от nx, имеются дополнительные правила проверки качества кода и более строгие правила тайпскрипт конфига, для более быстрого старта разработки приложений на данной архитектуре были разработаны наборы схематиков @nestjs-mod/schematics для генерирования кода.

Схематик для создание базового приложения

Команды для создания пустого NestJS-mod приложения

# Создать пустой nx проект
npx --yes create-nx-workspace@17.2.8 --name=project-name --preset=empty --interactive=false --nx-cloud=false

# Перейти в созданную папку
cd project-name

# Установить схематик для генерации NestJS-mod приложения
npm install --save-dev @nestjs-mod/schematics@latest

# Создать приложение NestJS-mod
./node_modules/.bin/nx g @nestjs-mod/schematics:application --directory=apps/app-name --name=app-name --projectNameAndRootFormat=as-provided --strict=true

Пример сгенерированного приложения: https://github.com/nestjs-mod/nestjs-mod-example/tree/master/apps/app-name

Запуск созданного приложения в режиме разработки

# Подготовить все файлы
npm run manual:prepare

# Запустить приложения в watch-режиме
npm run serve:dev:app-name

Сборка и запуск приложения в продакшен режиме

## Собрать приложения
npm run build:prod:app-name

## Запустить собранное приложения
npm run start:prod:app-name

Интегрированы:

Схематик для создания типовой библиотеки NestJS-mod

Команда для создание пустой библиотеки

# Создание NestJS-mod библиотеки
./node_modules/.bin/nx g @nestjs-mod/schematics:library feature-name --buildable --publishable --directory=libs/feature-name --simpleName=true --projectNameAndRootFormat=as-provided --strict=true

Пример сгенерированной библиотеки: https://github.com/nestjs-mod/nestjs-mod-example/tree/master/libs/feature-name

Интегрированы:

Реализованные на данный момент NestJS и NestJS-mod модули

Системные модули

  • DefaultNestApplicationInitializer - Инициализатор приложения NestJS по умолчанию (краткое описание).

  • DefaultNestApplicationListener - Прослушиватель приложений NestJS по умолчанию (краткое описание).

  • ProjectUtils - Утилиты для настройки глобальных параметров приложения, таких как имя проекта, описание и параметры валидации переменных окружения и переменных конфигурации модулей и приложения (краткое описание).

  • NestjsPinoLogger - Pino логер (Обертка для https://www.npmjs.com/package/nestjs-pino, (краткое описание).

  • TerminusHealthCheck - Обертка над NestJS модулем для проверки доступности приложения (краткое описание).

  • DefaultTestNestApplicationCreate - Модуль для создания тестового приложения NestJS (краткое описание).

  • DefaultTestNestApplicationInitializer - Модуль для запуска тестового приложения на NestJS (краткое описание).

Модули ядра

Модули инфраструктуры

  • DockerCompose - Модуль для генерации docker compose файла (краткое описание).

  • DockerComposePostgreSQL - Модуль для описания docker compose сервиса с базой данных postgres, будет использован DockerCompose модулем при генерации итогового compose файла (краткое описание).

  • Pm2 - Модуль для генерации конфигурации необходимой для запуска приложения через PM2 (краткое описание).

  • NestjsModAllReadmeGenerator - модуль для генерации документации по всей инфраструктуре (краткое описание).

Планы на будущее

  • Нужно будет декомпозировать код и типизацию улучшить - так-как при разработке данного проекта я поставил себе некий дедлайн, в который успешно смог вписаться, качество кода при этом сильно пострадало.

  • Написать больше тестов на основной функционал - тесты есть не на все возможные ситуации, некоторые параметры при использовании утилит проверялись вручную, а не автоматизировано через тесты. Модули которые лежат в репозитории https://github.com/nestjs-mod/nestjs-mod-contrib вообще не имеют тестов, все проверялось вручную.

  • Расширить генератор документации по инфраструктуре.

  • Добавить примеры использования для всех модулей и утилит - сейчас работу с ними можно изучить только через прочтение тестов, а нужно чтобы документация была в readme файле.

  • Добавить больше модулей оберток или написать свои реализации для типовых вещёй (работа с Redis, работа с Nodemailer и так далее).

  • Добавить модули для генерации пайплайн конфигураций gitlab-ci, bitbucket, jenkins

  • Добавить генератор для сборки докер образов и генерацию скриптов для разворачивания приложения в Kubernetes.

  • Написать консольное приложение для более удобной работы с схематиками.

  • Добавить возможность сохранять метаинформацию по модулям и их конфигурации в package.json и возможность развернуть всю кодовую базу и параметры развертывания и доставки имея всего лишь два файла package.json и .env файл.

  • Сделать облачное решение которое позволит визуально собирать приложение и включать в него все необходимые модули и группы модулей, которое можно развернуть в любом месте.

Заключение

Я не знаю на сколько такая архитектура приложения и утилиты которые в ней имеются смогут войти в реальный мир разработки на NestJS, но я сам все собственные проекты начал переводить на NestJS-mod, так-что проект точно не умрет ?

Ссылки

P.S.

Не судите строго за качество кода, в приоритете был скорейший выпуск в открытый доступ некой MVP версии данного проекта.

Проект в open source и если есть желание и время то я буду очень рад новым пулл реквестам и новым контребьютерам.

Хочу сказать спасибо https://github.com/ArgoN1ck за иконку проекта ? и спасибо https://github.com/AleksandrAlyokhin за конструктивные замечания по тексту данной статьи ?