19 - Монорепозиторий или нет
Монорепозиторий - это репозиторий сразу для нескольких проектов. Зачастую, такой подход используется, когда у нас не более 12 микросервисов
Плюсы:
- Переиспользование уже написанного кода, так как все сервисы находятся в одном месте
- Удобная работа с контрактами
- Удобно можно настроить все пайплайны и зависимости сборки проекта (больше плюс для DevOps-инжинера)
Минусы:
- Огромные объёмы проекта, что тормозит работу IDE
- Централизует все зависимости, что при переходе на новую версию того же NextJS или другого модуля создаст проблемы с обновлением
- Ограничение используемых языков
Чтобы получить все преимущества микросервисов, пользоваться монорепозиторием нельзя, но если нужно быстро билдить приложение и проще им управляться, то можно хранить все сервисы в одном репозитории.
Однако монорепозитории хорошо использовать, когда у нас мало сервисов, они написаны на одном языке и у нас много различных контрактов, чтобы использовать их между микросервисами.
Самые популярные утилиты для работы с монорепозиториями:
- NX
- Lerna
- npm (выбираем по умолчанию, если проект не имеет сложной архитектуры и имеет один фронт)
- turborepo
- rush
NX
:
- Очень легко с ним работать
- Очень удобные инструменты, которые позволяют локально запустить сразу несколько сервисов
- Имеет свой взгляд на работу с сервисами (имеет свои определённые модули для сборки определённых фреймворков и свои правила) - Webpack
- Работает сугубо на базе TS
Lerna
:
- Позволяет работать с любым сборщиком, с которым мы хотим работать
NX
не даст нам в монорепозитории использовать несколько разных версий того же NextJS
. Если нам нужно поменяться на более новую версию, то тут уже нужно будет обновить весь проект. Другими словами, мы должны поддерживать единый скоуп зависимостей и поддерживать чистоту кода.
20 - Обзор NestJS
Компоненты, которые мы имеем в несте:
Modules
- это строительные блоки в приложении, которые агрегируют в себе определённую логикуControllers
- это обработчики внешних событийProviders
- это компонент, который обеспечивает доступ приложения к внешним источникам данных (БД)Middleware
- это посредник, который внедряется в процесс запроса и обогащает функциональностьException filters
Pipes
Guards
Interceptors
Модули могут быть включены в другие модули
Модуль - это класс с декоратором модуля, который позволяет описать внутри этого декоратора все провайдеры, контроллеры, модули и другие зависимости, что он экспортирует
Все точки входа в приложение - это контроллеры, которые триггерят выполнение определённых операций
Так как контроллер - это входная точка в модуль, то его переиспользование в других модулях - невозможно
Декоратор @Controller()
помечает класс как контроллер, а так же выполняет DI-функцию
Правильный подход к работе с БД заключается в описании работы с репозиторием через класс и подключение этого класса через декоратор в категорию провайдеров
Так же благодаря тому, что мы работаем с модулями, мы можем использовать репозитории и в других модулях просто экспортируя их
Декоратор @Injectable()
обеспечит добавление класса в категорию провайдеров
Общая схема работы запроса выглядит подобным образом:
Middleware
- первый принимает в себя запрос и он может обрабатывать его, валидировать и так далееGuards
- это ограничитель, который зачастую используется для авторизации всех запросов с клиента (проверка валидности JWT, делать проверку аутентификации и так далее). Так же в гуарде можно произвести проверку на наличие соответствующего доступа у пользователя. По сути они могут ограничивать те или иные роуты для пользователяInterceptors
- внедряются до или после контроллеров, обрабатывая запросыPipes
- занимаются преобразованием запросов (например, может валидировать по декораторам то, что нам прислали)Controller
- сама наша конечная точка, вокруг которой построена данная архитектураException Filters
- обрабатывает все входящие ошибки из контроллера
Так же для реализации общения приложения с БД будет использоваться паттерн репозитория.
У неста имеется модуль nestjs-mongoose
, который позволяет описать модели в виде классов с декораторами. Эту схему можно будет с помощью декоратора @InjectModel
добавить в репозиторий и на выходе мы получим модель, с которой будет работать репозиторий
Паттерн же говорит нам, что для каждой такой модели должен быть свой репозиторий со своей входной точкой, с которой мы будем взаимодействовать. Мы не будем напрямую ходить в модель - мы будем ходить только в репозиторий этой модели
Паттерн Entity
говорит нам, чтобы мы использовали для общения между контроллером и репозиторием не какой-то конкретный объект по интерфейсу, а отдельную сущность в виде класса. Эта сущность будет повторять модель репозитория и будет содержать в себе определённую бизнес-логику
И примерно так будет выглядеть наш микросервис по работе с аутентификацией:
21 - Код Настраиваем nx monorepo
Первым делом нужно установить сам nx и cli неста
Далее нужно создать проект с микросервисами. Ему сразу можно задать пресет той технологии, что мы будем использовать.
И далее мы получаем структуру, где у нас в корне проекта находятся:
- все глобальные файлы стилей / базовый тсконфиг (от него экстендятся все остальные конфиги) / все тесты
- папка
apps
, которая хранит все микросервисы приложения - папка
libs
, которая хранит переиспользуемые данные для приложения
Так же имеется плагин NX console, который позволит сразу запускать все команды из nx.json
И тут нужно сразу сказать, что при использовании nx’a мы не теряем возможность пользоваться CLI’ками других модулей (тем же нестовским cli) - всё это у нас остаётся
Так выглядит запуск сёрвинга проекта:
Всё, что мы можем переиспользовать должно находиться в библиотеках.
libs / interfaces / src / lib / user.interface.ts
libs / interfaces / src / index.ts
Так выглядит структура созданной библиотеки:
И теперь мы можем спокойно импортировать из библиотеки любой модуль в любой микросервис нашего приложения
Происходит это потому, что в нашем базовом тсконфиге добавляется автоматически строчка, которая создаёт алиасы для нашего пути
И далее нам нужно создать модули, сервисы и контроллеры для отдельных частей приложения аккаунтов:
И установим зависимости для подключения аутентификации в проект и валидации:
Все те зависимости, что мы указали выше - устанавливаются глобально для всего проекта
22 - Код Создаём модели
Для начала создадим папку с нашими env
файлами envs
. В ней нужно будет создать envs/account.env
. Тут у нас будут храниться данные для подключения к базе.
Далее нам нужно написать конфиг для подключения к базе данных
apps / account / src / app / configs / mongo.config.ts
Тут мы должны подключить наши модули и в частности ConfigModule
, который должен прокидываться внутрь всех импортов (для доступа модулей к конфигу приложения) и MongooseModule
для подключения монги
apps / account / src / app / configs / mongo.config.ts
Интерфейсы пользователя, которые будут распространены по проекту благодаря их нахождению в папке lib
lib / interfaces / src / lib / user.interface.ts
Тут находится класс, который представляет собой модель данных пользователя находящегося в базе монги
apps / account / src / app / user / models / user.model.ts
Далее нужно создать сущность пользователя, которая сможет выполнять определённые действия над данными самого этого пользователя.
Сущность выполняет роль хранилища основных операций над данными пользователя
apps / account / src / app / user / entities / user.entity.ts
Далее нужно реализовать инжектируемый класс репозитория. Репозиторий работает чисто только с базой данных.
Так как это кастомная сущность, то впоследствии её нужно будет добавить в модуль пользователя.
apps / account / src / app / user / repositories / user.repository.ts
И уже тут финально подключаем все сущности к модулю самого пользователя
apps / account / src / app / user / user.module.ts