Як Turborepo падчырываецца ў NestJS Monorepo і калі яго можна прыняць за непатрэбны
Як Turborepo, pnpm workspaces і NestJS дзельняцьця работу ў TypeScript манорепа: спакеты, якія выкарыстоўваюцца ўсімі, графы задач, кэшаванне, рызыкі зв’язку і калі можна ўжо не выкарыстоўваць такія падходы.
Крыніцы: Turborepo + NestJS: The Monorepo Architecture That Makes Large TypeScript Systems Manageable
NestJS-аўтэндап часта пачынаецца як адна API-сервіса з адным файлам package.json, адной папкай node_modules і аднай CI-паіплайнам. Калі продукт расте, аспекты такія як аутантыкацыя, замовленні, платежы, запасы і прыемныя упаведамленні ператвараюцца ў окремыя сервісы, і задача пераходзіць з „як нам расшырыць гэты сервіс?“ на „як нам кераваць семью TypeScript-проектамі, не копіюючы тую ж настройку, типы і паіплайны семь разоў?“. У гэтым артыкуле пояснюецца, які вклад даўа кожны з монорепо, pnpm і Turborepo, як задачовы граф і кэш эканомяюць час, што можна безпечна дзеліцца, і калі настройка коштае больш, чым прыносіць.
Завучайце адно правілу ў всём. Turborepo не стварае мікросервісаў, не заменяе NestJS і сам па сабе не робіць архітектуру масштабавальной. Ён рашае болей вузкія проблема: коордынацыю работ у стыле build між багатымі пакетамі ў аднам рэпазітарыі.
З аднага сервісу да багатых рэпазітарыяў
Рэпазітарый аднойчы растучай платформы можа запрацаваць з папкамі па сервісу:
auth-service/
order-service/
payment-service/
inventory-service/
notification-service/
shipment-service/
api-gateway/
Раздзелэнне сервісаў спрыяе масштабаванню і адпаведнае распадзелцы праваў, але кожны сервіс тепер мае своия залежнасці, настройкі, процесы build, тэсты, правілы лінтингу і пайплайны.
Косцы коордынацыі у полірэпазітарыі
Уявіце типовую систему е-камеры, у якой гэйтвей працуе з сервісамі аутантыкацыі, замовленняў і продуктаў; сервіс замовленняў вызывае сервісы платежаў і запасоў, а платежы спрычыняюць выданне прыемлівых паведамленняў:
API Gateway
|
┌─────────────────┼─────────────────┐
↓ ↓ ↓
Auth Order Product
|
┌────────┴────────┐
↓ ↓
Payment Inventory
|
↓
Notification
Звычным першым крокам ў такіх ситуацыях яўляецца стварэнне для кожнай службы сваёй сабскрыпцы, тэхніка polyrepo:
github.com/company/auth-service
github.com/company/order-service
github.com/company/product-service
github.com/company/payment-service
github.com/company/inventory-service
github.com/company/notification-service
Это легітмная архітектура. Проблемы пачынаюцца, калі апускаешся да внутраней структуры кожной сабскрыпцы і бачыш, што тама павтараецца аднойчы той самы каркас:
package.json
tsconfig.json
eslint.config.js
Dockerfile
CI configuration
README.md
node_modules
І, што ўжо горш, тыя ж самыя фрагменты коду, якія патрэбны калькам службаў аднойчы:
DTOs
TypeScript types
logging utilities
configuration
authentication helpers
database utilities
Kafka/RabbitMQ abstractions
testing utilities
Тады кожная змяна у спільным матэрыяле стае задачай сінхронізацыі. Калі зміняецца спільны тип, якія сабскрыпцы трэба апдэйтаваць? Калі змінюецца залежнасць, якія службы патрэбуюць апградаціі? Калі змінюецца правіло перагляду коду або процес CI, сколькі копій трэба правіць і падтрымляць у ўзгоднасці? Колькість служб — не справжня проблема. Справжняя проблема — коордынацыя.
Monorepo — это стратэгія; Turborepo — это інструмент
Альтэрнатыва — хаваць усе прыкладныя програмы і спільны код у аднам рэпазітарые, пры чым прыкладныя програмы знаходзяцца пад apps/, внутрашні бібліятэкі — пад packages/, а настройкі рабочага прастору і заданняя — у корні:
ecommerce-platform/
│
├── apps/
│ ├── api-gateway/
│ ├── auth-service/
│ ├── order-service/
│ ├── product-service/
│ ├── payment-service/
│ ├── inventory-service/
│ └── notification-service/
│
├── packages/
│ ├── types/
│ ├── logger/
│ ├── config/
│ ├── database/
│ └── messaging/
│
├── package.json
├── pnpm-workspace.yaml
└── turbo.json
Такая структура — цэх манорэпазітарые: рашэнне пра тое, дзе знаходзіцца код. Turborepo — гэта інструмент, які коордынуе работу ўнутрь яго. Можна матыць адны без другога.
Што на самай працы робіць Turborepo
Turborepo — цэх система для складання коду для манорэпазітарыяў на JavaScript і TypeScript. Яна координуе заданняя, якія існуюць у калькі пакетаў адразу, такія як:
build
test
lint
type-check
dev
Без яго складанне калькі службаў значыць прабавацца да кожной з іх вручную. Спачатку служба аутэнтыкацыі:
cd apps/auth-service
pnpm build
потым іншыя по чарге (у фрагменте коду два каманды выкананыя на адной лініі; кожны cd і pnpm build павінны стаяць на сваёй лініі):
cd ../order-service
pnpm buildcd ../payment-service
pnpm build
За дапамою Turborepo вы адзначаеце задачы ўсёраз і запускаеце адну команду з корневага каталога:
turbo build
Пасля чаго ён вырашае, якія задачы неабходны, якія можна запускаць паралельна, і якія рэзультаты можна восстановіць з кеша замест таго, каб іх перыякшваць.
Структураванне прыложэнняў NestJS і спільных пакетаў
Версія рэпазітарыю, адпрацоўваная для працы ў працэсе вырабніцтва, даўа кожнаму прыложэнню Nest свой src/ і package.json, такім чынам кожна прыложэнне стае самастоятельным пакетам рабочага простору:
ecommerce-platform/
│
├── apps/
│ │
│ ├── api-gateway/
│ │ ├── src/
│ │ └── package.json
│ │
│ ├── auth-service/
│ │ ├── src/
│ │ └── package.json
│ │
│ ├── order-service/
│ │ ├── src/
│ │ └── package.json
│ │
│ └── payment-service/
│ ├── src/
│ └── package.json
│
├── packages/
│ │
│ ├── types/
│ ├── logger/
│ ├── config/
│ ├── database/
│ └── messaging/
│
├── turbo.json
├── pnpm-workspace.yaml
└── package.json
Канцэптуальна існуе два слоі: прыложэнняя, якія ўособляюцься сэрвісамі NestJS і выконваюць бізнес-функцыяў, і пакеты, якія маюць у сабе код, які можна викорыстоўваць знову.
Monorepo
|
┌─────────┴─────────┐
↓ ↓
Apps Packages
| |
NestJS Shared Code
Прыложэнняя реалізуюць функцыяй, пакеты запаводжаюць спільныя елементы для будовы, а Turborepo коордынуе задачі між ямі.
Внутрашняе пакеты як едыны адказвальны источнік
Сярод найбольшых ўплыўнасцей монорепа ў павсякдзеннай працы є внутршнія пакеты. Напрыклад, пакет з типамі можа мястіць модэлі, якімі пагадваюцца калькі служб:
packages/
└── types/
├── src/
│ ├── user.ts
│ ├── order.ts
│ ├── payment.ts
│ └── pagination.ts
└── package.json
Кожная служба Nest можа запрашваць іх па імени пакета, якое вырашваецца через прастор работы, а не через рэжыстр:
import { OrderStatus } from '@repo/types';
замест таго, каб клікаць той самы enum у кожной службе і спадзявацца, што копіі ніколі не будуць разныя:
enum OrderStatus {
PENDING = 'pending',
PAID = 'paid',
CANCELLED = 'cancelled',
}
Калі enum атрыбуецца значэнне, кампайляр пазначае ўсія афектаваныя месцы ў той жа змянэнні.
Аднароджанне кантрактаў не тое ж, што аднароджанне праваеўнасці
У монорепа лёгка аднароджваць ресурсы, і самэ гэта ўсёлікавае патрэбу ў дисцыпліне. Логіка домэна службы замовлення належыць самай службе замовлення:
order-service
↓
order-domain
Яна не должна таямніча перайсці ў пакет:
packages/
└── order-domain/
які потым запрашваюць іншыя службы:
payment-service
inventory-service
notification-service
Калі платеж, запасы і код упавядомлення залежаць ад внутрашніх элементаў домэны замовлення, змяна правіл замовлення можа нашкодзіць трохі незалежным реалізацыям. Золатае правила: свядома дзеліцца інфраструктурой і контрактамі, але ніколы не дзеліцца правамі на ведачаў бізнесу за значчынкам. Разумныя кандыдаты на спільныя пакеты выглядаюць так:
@repo/types
@repo/logger
@repo/config
@repo/testing
@repo/messaging
Бізнес-логіка зазвычай павінна застацца ў службе, якая яе ведае. Чырвоныя деталі гэтага компромісу можна пазнакоміцца ў duplication versus coupling: deciding when shared code should exist.
Граф залежнасцяў вялікі, што будзе выканана
Turborepo чытае зв’язкі залежнасцяў, заданыя ў файле package.json кожнага прастору роботы. У гэтым прыкладзе оба сервісу залежная ад пакета types, а сервіс order таксама залежнай ад пакета базы дадзеных:
@repo/types
/ \
/ \
↓ ↓
auth-service order-service
|
↓
@repo/database
Паколькі Turborepo знае гэтую структуру, якщо вы зменіце файл унутры сервісу order:
apps/order-service/src/orders/order.service.ts
немае прычыны перзбудаваць некаляжны сервіс, такі як:
notification-service
За дапамогою фільтраў (напрыклад turbo build --filter=...) можна обмежыць запуск толькі змененымі пакетамі і ўсіма, якія ад них залежная.
Кэшаванне: прыпіненне виконання вялікі часткі роботы
Калі вы першы раз запускаеце процес будавання:
turbo build
кожная неабходная задача фактычна выкананая:
auth-service → BUILD
order-service → BUILD
payment-service → BUILD
inventory-service → BUILD
Turborepo фіксуе даныя вхідных элементаў кожнай задачы (файлы-выхаднікі, залежнасці, настройкі, актуальныя зменныя сераўысу) і зберагае выхідныя даны та логі. Запустіце тую ж самую команду зноў, не мяняючы нічога значныя:
turbo build
А рэзультат выглядае так:
auth-service → CACHE HIT
order-service → CACHE HIT
payment-service → CACHE HIT
inventory-service → CACHE HIT
Выхідныя даны былі восстановлены з кэшу. Ён можа працаваць толькі тады, калі Turborepo ведае, якія файлы стварае задача; гэта прызначана наявнасцю палаткі outputs у файле turbo.json (для складання Nest зазвычай dist/**). Задача без заданых выхідных даных може адзвярнуцься, што кэш быў знайдзены, але ў вас не застануцца рэзультаты складання.
Чаму CI мае найбольшыя выгоды
Простая паіплайн-система перазрабоцьвае весь процес праз кожны push:
Git Push
↓
Install dependencies
↓
Build everything
↓
Test everything
↓
Lint everything
↓
Deploy
Калі Turborepo выкорыстоўваецца ў процесе, кожны задача спачатку пераглядаецца ў зв’язку з графам і кэшам, і выконваюцься толькі тыя задачы, чыя вхідныя даны зменіліся:
Git Push
↓
CI
↓
Turborepo
↓
┌─────────┴─────────┐
↓ ↓
Dependency Task Graph
Analysis ↓
Cache Check
↓
┌─────────┴─────────┐
↓ ↓
Cached Execute
Tasks Tasks
У великіх репазітарыях это падрыхляе змену вельмі большай колькасці зайвай роботы CI.
Локальны кэш протыяў уздалённага кэшу
Існуе два роўня кэшавання:
- Локальны кэш. Ваша сопрацоўчыца знову выкарыстоўвае рэзультаты задач, якія ўжо былі выкананы, што прышвартавае ўседзённы развіццё.
- Уздалённы кэш. Спяльваны кэш дазволяе разработчыкам і машынам CI выкарыстоўваць рэзультаты аднае другой.
Уздалённая наладка выглядае так:
Developer A
│
↓
Remote Cache
↑
│
Developer B
↑
│
CI
Якщо разработчык уже створыў сервіс, а пазней CI прабуе выконаць тыя ж умовы задання, CI завантажвае рэзультат замест таго, каб перастраіваць його. Спрыяйце даўнему кэшу як часткі вашай ланцоўкі поставок: контролюйце, хто можа ў яго пісаць, адтак як заражаны файл будзе выдаўаны всім.
Выконанне незалежных задач паралельна
Дзеяныя сервісы не залежаць ад рэзультатаў стварэння іншых сервісаў:
auth-service
order-service
product-service
Выконваюцца па спрабе, кожны чакае на предыдушны:
auth → finish
↓
order → finish
↓
product
Turborepo можа выконваць іх адночасна, пры чым глобальны ліміт — наявная колькасць CPU:
┌── auth ────┐
│ │
turbo ──┼── order ───┼──→ Done
│ │
└── product ─┘
Апісанне адносаў межы задач
Тая ж схема керування прыменяецца да всіх типаў задач, не толькі да процэсаў стварэння:
turbo build
turbo test
turbo lint
turbo typecheck
Связі межа пакетамі заявляюцца ў turbo.json. У наведзенай выкладцы "^build" означае "спачатку збудуўці залежнасці гэтага пакета", таму спяльныя пакеты компілююцца ранейш за сервісы, якія іх імпортуюць. Задача test залежыць ад задачі build без знака карэткі, який паводзіцца на збудову таго ж пакета. Задачы lint і typecheck не маюць залежнасцей і можуць запускаться адразу паралельна.
{
"tasks": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {},
"typecheck": {}
}
}
Вы описваеце, як задачы залежаць адзін ад другога, а Turborepo стварае план выконання. У рэальным проекте таксама трэба дадаць элемент outputs да задач, якія можна кэшаваць, як описана вышыней, і пераканацца, што назва кожной задачы падходзіць да скрыпты ў файлах package.json пакетаў працовай зоны.
pnpm і Turborepo выканаюць разныя задачы
Этыя два элемента з’являюцца так часта, што можа здавацца, як адна засоба:
Turborepo + pnpm
Адзін з іх — адзін слой. pnpm караце пакеты і прасторы працы: встановляе залежнасці, супяжнюе внутрэшныя пакеты і стварае строгіяй порядак у node_modules. Turborepo координуе задачы і кэшуе ўсія яны на базе гэтага прастора працы.
pnpm
│
Workspace Management
│
↓
┌─────────────┐
│ Monorepo │
└──────┬──────┘
│
↓
Turborepo
│
┌───────┼────────┐
↓ ↓ ↓
Build Test Lint
Ёсць можлівасць болей дакладна пазнакоміцца з самым pnpm: npm vs pnpm: сховышча, скорасць і практычныя наследкі.
Чаму проста не викорыстаць вбудованы режым монорепо ў Nest?
Nest CLI мае свой саблей мод гэтаго типу з прыемкамі та бібліятэкамі, таму дадзенне Turborepo патрабуе прамовы. Разлік заключаецца у сферы дзейна. Рабочая прастора Nest разумее толькі проекты Nest. Turborepo працуе з будзь-якімі пакетамі на JavaScript чыста TypeScript у рабочай прасторы. Калі ў рэпазітарыі стае больш, чым толькі сервісы Nest, напрыклад, фронтэнды чыста працоўнікі Node:
apps/
├── web/ # Next.js
├── admin/ # Next.js
├── api-gateway/ # NestJS
├── order-service/ # NestJS
└── worker/ # Node.js
а таксама спяльныя пакеты для UI, типав і налашоўкі:
packages/
├── ui/
├── types/
├── config/
└── eslint-config/
вы больш не керуеце проектам Nest. Вы керуеце платформай TypeScript, і самэ гэта робіць Turborepo праўдападным выборам.
Turborepo, Nx чыста Nest CLI
Не існуе ўніверсальна правильная выбор. Як загальныя напамінання: режым monorepo у Nest — гэта простейшы варыянт, калі ўсё здзейсняецца за дапамогою Nest. Turborepo — цэлая тонкая шар з адбывання задач і кэшавання, якая дадаўаецца на верхній слой працовых прастораў вашага менеджера пакетаў. Nx таксама з’являе граф проекта і можлівасць кэшавання, а таксама додае большы набор плагінав, генератораў коду і стандартоў, якія дексты команд ценяць, а іншыя счыліяць занадта важкімі. Выбірайце на аднойчынку з вашымі рэальнымі патрэбамі, а не на аднойчынку з тым, які інструмент прывлекае найбольшую увагу ў соцыяльных меражах.
Калі Turborepo — гэта занадто
Падазроўваючы, што весь система — цэлая Nest API, падтрымваная PostgreSQL і Redis:
NestJS API
↓
PostgreSQL
↓
Redis
з маленькай командай і адним об’ектам, які можна разместіць:
1 application
5 developers
1 deployment
Turborepo, верагодна, непатрэбны. Звычайны рэпазітарый Nest — гэта простае рашэння, а введэнне всіх гэтых элементаў:
Turborepo
pnpm workspace
multiple packages
complex task graph
Гэта дадзець дапамогу канцэптыўнай навантажэнню, але не прыносіць якога-лібо рэзультата. Інструменты здобываюць свое месца тады, калі проблема яго прабачае.
Калі ён почынае даваць рэзультаты
Ситуацыя змінюецца, калі адночасна выпалоўвае кальколька такіх умов:
Multiple applications
+
Shared packages
+
Shared tooling
+
Repeated CI tasks
+
Large TypeScript codebase
Тыпавы прыклад — платформа з фронтэндамі на Next.js, API-мі на Nest і фонавымі працоўнікамі, якія ўсе выкарыстоўваюць спакеты, якія дзеляцца між сабою:
Platform
│
┌──────────────┼──────────────┐
↓ ↓ ↓
Next.js NestJS Workers
Web APIs Jobs
│ │ │
└──────────────┼──────────────┘
↓
Shared Packages
Зрэлыя структуры з чыста выражанымі абавязкамі
Зрэлая платформа Nest можа дадаць адзінокую зону для інфраструктуры пашыле ўсіх аплікацый і спакетаў:
platform/
│
├── apps/
│ ├── api-gateway/
│ ├── auth-service/
│ ├── user-service/
│ ├── order-service/
│ ├── inventory-service/
│ ├── payment-service/
│ └── notification-service/
│
├── packages/
│ ├── config/
│ ├── logger/
│ ├── types/
│ ├── database/
│ ├── messaging/
│ └── testing/
│
├── infrastructure/
│ ├── docker/
│ └── deployment/
│
├── turbo.json
├── pnpm-workspace.yaml
└── package.json
Кожны інструмент мае адну задачу. NestJS — это фрэймворк для аплікацый:
NestJS
↓
Application framework
А засталія шары дзелююцца так (у фрагменте коду калькі лісты выкананы разам; спрыяце іх як pnpm для залежнасцей і рабочых прастораў, Turborepo для оркестрацыі задач і кэшавання, Docker для контейнерайзаціі, Kafka або RabbitMQ для асінхронной перадачы паведамленняў, а таксама PostgreSQL або Redis для зберагчання дадзэнняў і кэшавання):
pnpm
↓
Dependency & workspace managementTurborepo
↓
Task orchestration + cachingDocker
↓
ContainerizationKafka / RabbitMQ
↓
Asynchronous communicationPostgreSQL / Redis
↓
Data & caching
Turborepo – гэта не ваша інфраструктура, не ваш брокер паведамленняў і не платформа для развярнення. Це шар будовы і адканання задач, який абгружае монорепо.
Адна база дадзэння не значыць адна развяртка
Частаю прычынай абавярэння є тое, што размешчанне всього ў адной базе дадзэння паслабляе незалежнасць, якую мяркаваліся забезпечыць мікросервісы. Аднак гэта не так. Месца, дзе знаходзіцца код, і спосаб яго развяртання – гэта аднае рашэнні. Адна база дадзэння можа мястіць многа сервісаў:
One Git repository
│
├── auth-service
├── order-service
├── payment-service
└── notification-service
Кожны з іх яшчэ выкладваецца як самастая контейнер:
auth-service → Container A
order-service → Container B
payment-service → Container C
notification → Container D
і расшырваецца за сваімі правіламі:
order-service × 10
payment-service × 5
notification × 3
Манорепа не ўсё тое ж самае, што маноліт, а полірепа не дае вам автоматычна мікросервісоў. Це незалежныя выміры. Тое, што вам патрэбна, — це система CI, якая выкладвае толькі тыя сервісы, чыё код або залежнасці зменіліся, што стаўляе можлівым граф залежнасцяў.
Скрытая абарона: загальны пакет, які працоўвае з усім
Лёгкая аддача працюе ў обох направленнях. Часта все пачынаецца няшкодна з аднаго пакета:
packages/common
які поступова скарабатвае падпамогі для кожнай домэны:
packages/common
├── AuthUtils
├── OrderUtils
├── PaymentUtils
├── InventoryUtils
├── NotificationUtils
├── DatabaseUtils
└── EverythingUtils
пакуль кожны сервіс не стане залежным ад яго. У такі момент вы створылі распрацоўваны маноліт унутрь манорепы: змена ў адной падпамозе вымагае перзбірання, ператэставання і практычна выкладвання всього. Найвышэйшыя папкі яшчэ выглядаюць апранутымі:
apps/
packages/
Але архітектура, яка знаходзится падаю, ў тыску спаяная. Следзьце за строгім правілам: дзеліцца інфраструктурой, контрактамі та справжняя універсальная інструменталкаю; логіку домэны залучайце ў самы сервіс, які яё керуе.
Роўневая ментальная модэль
Дапамагае уявіць сістэму як шары: дзеўерсныя домэны — на верхнім роўні, спакульная інфраструктура та контракты — нижэй, а інструменталкая база — у самым ніжнем роўні:
Business Domains
│
┌───────────────┼───────────────┐
↓ ↓ ↓
Order Payment Inventory
│ │ │
└───────────────┼───────────────┘
↓
Shared Contracts
│
↓
Shared Infrastructure
│
↓
Monorepo Tooling
│
↓
Turborepo
Turborepo знаходзится майже ў ніжней часты. Яго задача — падтрымваць вашу архітектуру, а не формаваць яе.
Рашэнне пра адпрыем яго
Адпаведна развярніце чатыры пытанні.
Чы ў вас больш ад одной прыкладнай програмы?
Якщо няма, адказ просты:
Keep it simple.
Якщо є, пераходзьце да наступнага пытання.
Чы прыкладныя програмы дзеліцца значнай часткай інструменталкі або коду?
Якщо няма:
Polyrepo may be perfectly reasonable.
Якшы є:
Monorepo becomes attractive.
Чы горткі час, час тэставання і час перагляду коду стаюць дорогімі?
Якщо так:
Turborepo's orchestration and caching become valuable.
Чы вы запускаеце Next.js, NestJS, працоўнікі і спяльныя пакеты TypeScript адночасна?
Якщо так:
Turborepo becomes a strong candidate.
Прыведзенне ў прыгожданне — гэта справжня выгода
Швальнасць — це парадоксальны эфект. Справжняя цэннае — гэта тое, што велікі репазітары стае прыведзенным у прыгожданне. Калі колькасць аплікацыяў зростае, хтось павінен адпавядаць на кожную змяну:
What changed?
і на набор пытанняў, якія вылучаюцца пасля (у варыянтным кодзе яны выкананы ў адной лініі): што павінна быть перзбудавана, шта павінна быць ператэставана, што можа выканавацца паралельна, шта можа быць взята з кэшу, і якія пакеты залежаць ад змены:
What needs to rebuild?What needs to be tested?What can run in parallel?What can come from cache?Which packages depend on this change?
Без інструментаў інжынеры адпавядаюць на гэтыя пытанні вручную; з системай будовы монорепазітарыя большая частка з гэтага вылучаецца з графа.
Ключовыя выводы
Turborepo не будзе вылечваць проблемы з межамі абслюйнацыйных сэрвісаў, слабым дзеяннем базы дадзеных, занадта тыповым з’яўленнем компанентаў, паслаблёнымі контрактамі API, неяснасцю ў вопысе прыналежнасці домэну чы абмеркаванаю структурой мікросэрвісаў. Ён проста дапамагае кераваць растучым рэпазітарыем на TypeScript. Весь стак падае ў адну кантэкстную картыну:
Business Architecture
│
↓
NestJS Apps
│
↓
Shared Packages
│
↓
pnpm
│
↓
Turborepo
│
┌──────────┼──────────┐
↓ ↓ ↓
Build Test Lint
│ │ │
└──────────┼──────────┘
↓
CI/CD
- Монорэпазітарый выбірае, дзе будзе знаходзіцца код; pnpm керуе рабочай прасторам; Turborepo організавае та кэшуе задачы; NestJS стварае сэрвісы. Хавайце гэтыя ролі разнымі.
- Дзеліцеся абстракцыямі типаў, налашчэнняў, логавання і карысцявання паведамленнямі; хавайце логіку домэну разам з сэрвісам, які яе керуе, ўтрымуючыся ад стварэння распрастранёнага моноліту.
- Адзначайце зв’язкі межаў задач за дапамогою
dependsOnі рэзультаты ўturbo.json, каб кэшаванне было як эфектыва, так і правільна. - Структура рэпазітарыя і развёртыванне є незалежнымі: кожны сэрвіс все равно можа быць развёрнуты і масштабаваны самастоятельна.