<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Gyber</title><subtitle>| Calamitas virtutis occasio |</subtitle><author><name>Gyber</name></author><id>https://teletype.in/atom/experiment</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/experiment?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@experiment?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=experiment"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/experiment?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-05T15:21:24.610Z</updated><entry><id>experiment:nest-js-progressive-arch</id><link rel="alternate" type="text/html" href="https://teletype.in/@experiment/nest-js-progressive-arch?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=experiment"></link><title>NestJS Backend: опыт, архитектура и лучшие практики для зрелых проектов</title><published>2025-05-01T02:36:45.896Z</published><updated>2025-05-01T06:07:00.580Z</updated><summary type="html">Backend — это не просто набор функций, это инфраструктура доверия, ответственности и предсказуемости. Это фундамент, на котором строится бизнес, команда, будущее продукта.</summary><content type="html">
  &lt;h2 id=&quot;em5t&quot;&gt;Вступление: зрелость как инженерный выбор&lt;/h2&gt;
  &lt;p id=&quot;C5Th&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;aQG8&quot;&gt;&lt;em&gt;В мире разработки слишком часто побеждает спешка. Мы привыкли “делать MVP”, “выкатить как-нибудь”, “потом разберёмся”. Но любой, кто хоть раз сталкивался с поддержкой растущего продукта, знает: хаос не прощает легкомыслия. &lt;/em&gt;&lt;/h3&gt;
  &lt;p id=&quot;WNOG&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;LKEF&quot;&gt;&lt;em&gt;&lt;u&gt;Архитектура — это не набор функций, это инфраструктура доверия, ответственности и предсказуемости - фундамент, на котором строится бизнес, команда, будущее продукта.&lt;/u&gt;&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;7MgL&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;kR4y&quot;&gt;В какой-то момент мы с командой осознали: если мы хотим не просто “выживать”, а развиваться, нам нужно не латать старое, а строить новое — осознанно, системно, с прицелом на годы вперёд.  &lt;br /&gt;Мы выбрали путь зрелой архитектуры, где каждый модуль, каждый слой, каждый паттерн — не случайность, а результат анализа, обсуждений и опыта.&lt;/p&gt;
  &lt;p id=&quot;LFHr&quot;&gt;NestJS стал для нас платформой для внедрения инженерной культуры: модульность, строгая типизация, прозрачность зависимостей, поддержка лучших практик из мира TypeScript и Node.js.&lt;/p&gt;
  &lt;p id=&quot;8cJW&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;ZDVV&quot;&gt;Для кого эта статья&lt;/h3&gt;
  &lt;h3 id=&quot;Wuyp&quot;&gt;&lt;em&gt;&lt;code&gt;- от хаоса к гармонии&lt;/code&gt;&lt;/em&gt;&lt;/h3&gt;
  &lt;p id=&quot;2JWX&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;BFMj&quot;&gt;Когда-то мы начинали с быстрых MVP: “главное, чтобы заработало”. Но время шло, проекты росли, а с ними росли и требования — к скорости, безопасности, масштабируемости. И вот мы оказались на распутье: либо продолжать латать дыры, либо построить что-то по-настоящему крутое. Мы выбрали второе.&lt;/p&gt;
  &lt;p id=&quot;ftHd&quot;&gt;NestJS стал нашим верным спутником в этом приключении. Этот фреймворк — как швейцарский нож для разработчика: модульный, строгий, с поддержкой TypeScript и кучей готовых инструментов. Но даже с таким помощником без правильного подхода можно заблудиться. Поэтому мы взялись за дело с умом: продумали архитектуру, внедрили лучшие практики и отточили процессы. И знаете что? Получилось нечто, чем хочется поделиться с вами.&lt;/p&gt;
  &lt;p id=&quot;oPYE&quot;&gt;Эта статья содержит опыт, выстраданный через десятки итераций, бессонные ночи и горячие споры в команде. Здесь вы найдёте всё: от структуры проекта до мелочей вроде логирования и тестов. А ещё — немного вдохновения, чтобы ваш следующий проект стал шедевром.&lt;/p&gt;
  &lt;p id=&quot;tOzA&quot;&gt;Эта статья пишется для тех, кто устал от хаоса и хочет порядка. Для тех, кто строит не “ещё один сервис”, а платформу, которую не стыдно показать коллегам, инвесторам, самому себе через год.  &lt;/p&gt;
  &lt;p id=&quot;Vm6c&quot;&gt;&lt;br /&gt;Здесь не будет банальных советов и поверхностных решений. Только проверенные подходы, реальные грабли, архитектурные решения, которые выдержали нагрузку и время. Это систематизация опыта: от структуры папок до CI/CD, от DTO-first API до Docker-окружения, от логирования до тестов.  &lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;vTVm&quot;&gt;&lt;strong&gt;&lt;em&gt;Здесь вы найдёте не только “как”, но и “зачем” — мотивацию, причины, последствия каждого решения.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;vYpi&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gTXS&quot;&gt;Если вы ищете не волшебную таблетку, а рабочую систему — добро пожаловать.  &lt;br /&gt;Пусть ваш backend станет  надёжным и удобным для команды, прозрачным для бизнеса, готовым к росту и переменам.&lt;/p&gt;
  &lt;p id=&quot;BtK0&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;f6bb&quot;&gt;Введение:&lt;/h3&gt;
  &lt;p id=&quot;a7It&quot;&gt;В современном мире backend-разработки требования к качеству, безопасности и поддерживаемости кода постоянно растут. Мы прошли путь от “быстрого MVP” до архитектуры, которая выдерживает нагрузку, легко расширяется и радует как разработчиков, так и бизнес.  В этой статье я расскажу, как мы шаг за шагом строили современный backend на NestJS, довели его до уровня, соответствующего требованиям крупных production-проектов, и какие подходы, паттерны и инструменты мы внедрили для удобства, безопасности и масштабируемости.&lt;/p&gt;
  &lt;p id=&quot;IvvZ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;h1cA&quot;&gt;&lt;strong&gt;Статья будет полезна:&lt;br /&gt;&lt;/strong&gt;- начинающим и опытным backend-разработчикам,&lt;br /&gt;- тимлидам и архитекторам,&lt;br /&gt;- всем, кто хочет понять, как строить поддерживаемые и надёжные серверные приложения.&lt;/p&gt;
  &lt;p id=&quot;dqkA&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;LKsT&quot;&gt;1. Архитектура проекта: фундамент для роста&lt;/h2&gt;
  &lt;p id=&quot;Gdx5&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;DsKh&quot;&gt;1.1. Модули и слои: разделяй и властвуй&lt;/h3&gt;
  &lt;p id=&quot;f3KD&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;p0Xf&quot;&gt;&lt;em&gt;Всё начинается с архитектуры. Мы строим проект по принципу “один бизнес-домен — один модуль”.  &lt;br /&gt;Это значит, что у нас есть отдельные модули для пользователей, проектов, аутентификации и т.д.&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;Tmgo&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;RobK&quot;&gt;&lt;strong&gt;В каждом модуле:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;SbLX&quot;&gt;- &lt;strong&gt;controllers/ &lt;/strong&gt;— только маршрутизация и валидация входных данных. Здесь нет бизнес-логики!&lt;/p&gt;
  &lt;p id=&quot;gisc&quot;&gt;&lt;br /&gt;- &lt;strong&gt;services/ &lt;/strong&gt;— бизнес-логика, работа с БД, валидация бизнес-правил. Только здесь решается, “можно ли” и “как именно”.&lt;/p&gt;
  &lt;p id=&quot;be6t&quot;&gt;&lt;br /&gt;- &lt;strong&gt;dto/&lt;/strong&gt; — только DTO, специфичные для этого модуля. DTO — это “контракт” между backend и frontend.&lt;/p&gt;
  &lt;p id=&quot;qh3N&quot;&gt;&lt;br /&gt;- &lt;strong&gt;entities/ &lt;/strong&gt;— только TypeORM-сущности. Это “отражение” таблиц в базе.&lt;/p&gt;
  &lt;p id=&quot;NeQM&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;OKLs&quot;&gt;&lt;strong&gt;Пример структуры:&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;KrYx&quot;&gt;&lt;code&gt;src/&lt;br /&gt;  modules/&lt;br /&gt;    users/&lt;br /&gt;      controllers/&lt;br /&gt;      services/&lt;br /&gt;      dto/&lt;br /&gt;      entities/&lt;br /&gt;    projects/&lt;br /&gt;    auth/&lt;br /&gt;  shared/&lt;br /&gt;    dto/&lt;br /&gt;  common/&lt;br /&gt;    filters/&lt;br /&gt;    utils/&lt;br /&gt;    dto/&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;UVM2&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Почему это важно?&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;Wzth&quot;&gt;&lt;strong&gt;&lt;br /&gt;&lt;em&gt;&lt;u&gt;Такой подход позволяет легко масштабировать проект, добавлять новые фичи, не боясь “сломать всё”. Каждый модуль — как мини-приложение, но с едиными стандартами.&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;rKoE&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;hGWL&quot;&gt;1.2. Shared и Common: не дублируй, а делись&lt;/h3&gt;
  &lt;p id=&quot;COur&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;bo1N&quot;&gt;В зрелом проекте архитектура должна не только обеспечивать масштабируемость, но и минимизировать дублирование кода, облегчать сопровождение и ускорять разработку новых фич. Для этого мы чётко разделяем два слоя: &lt;strong&gt;shared&lt;/strong&gt; и &lt;strong&gt;common&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;Dzv0&quot;&gt;&lt;strong&gt;Shared/&lt;/strong&gt; — библиотека бизнес-логики&lt;/p&gt;
  &lt;p id=&quot;aGcv&quot;&gt;Папка &amp;#x60;shared/&amp;#x60; — это не “склад” общих файлов, а настоящая библиотека бизнес-типов и DTO, которые используются в нескольких модулях.  Здесь живут те сущности, которые отражают бизнес-реальность и нужны сразу в нескольких частях приложения.&lt;/p&gt;
  &lt;p id=&quot;xnuQ&quot;&gt;&lt;strong&gt;Что кладём в shared:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;quuT&quot;&gt;- DTO для пагинации (&amp;#x60;PaginateDto&amp;#x60;, &amp;#x60;PaginatedResult&amp;lt;T&amp;gt;&amp;#x60;)&lt;/p&gt;
  &lt;p id=&quot;9CFQ&quot;&gt;&lt;br /&gt;- Общие идентификаторы (&amp;#x60;IdentifierDto&amp;#x60;)&lt;/p&gt;
  &lt;p id=&quot;JG4l&quot;&gt;&lt;br /&gt;- Базовые бизнес-ответы (например, &amp;#x60;SuccessResponseDto&amp;#x60;, &amp;#x60;ErrorResponseDto&amp;#x60;)&lt;/p&gt;
  &lt;p id=&quot;Rf3u&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;pE1E&quot;&gt;-  Общие типы и интерфейсы, которые описывают бизнес-логику (например,      &amp;#x60;UserRole&amp;#x60;, &amp;#x60;StatusEnum&amp;#x60;)&lt;/p&gt;
  &lt;p id=&quot;eC9x&quot;&gt;&lt;br /&gt;- DTO для фильтрации, сортировки, поиска, если эти механизмы повторяются в разных модулях&lt;/p&gt;
  &lt;p id=&quot;mdtV&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;sBZW&quot;&gt;&lt;strong&gt;Зачем это нужно: &lt;br /&gt;&lt;/strong&gt;Когда бизнес-логика повторяется в разных модулях (например, пагинация нужна и для пользователей, и для проектов, и для заказов), мы не копируем код, а используем единый контракт. Это гарантирует согласованность API, облегчает поддержку фронта и ускоряет внедрение новых сущностей.&lt;/p&gt;
  &lt;p id=&quot;Np7A&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;yagw&quot;&gt;&lt;strong&gt;Пример:&lt;br /&gt;&lt;/strong&gt;&lt;code&gt;// shared/dto/paginate.dto.ts&lt;br /&gt;export class PaginateDto {&lt;br /&gt;  page?: number = 1;&lt;br /&gt;  limit?: number = 10;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;// shared/dto/paginated-result.dto.ts&lt;br /&gt;export interface PaginatedResult&amp;lt;T&amp;gt; {&lt;br /&gt;  items: T[];&lt;br /&gt;  total: number;&lt;br /&gt;  page: number;&lt;br /&gt;  limit: number;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;jHkf&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;NRyW&quot;&gt;&lt;strong&gt;Common/ — инфраструктурный слой&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;RwGi&quot;&gt;Папка &amp;#x60;common/&amp;#x60; — это технический фундамент приложения. Здесь лежит всё то, что не связано напрямую с бизнес-логикой, но обеспечивает стабильную работу, безопасность и удобство разработки.&lt;/p&gt;
  &lt;p id=&quot;BLVb&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;A8es&quot;&gt;&lt;strong&gt;Что кладём в common:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;0kZM&quot;&gt;- Глобальные фильтры ошибок (&amp;#x60;HttpExceptionFilter&amp;#x60;)&lt;/p&gt;
  &lt;p id=&quot;5Bi3&quot;&gt;&lt;br /&gt;- Middleware (например, логирование, CORS, трекинг запросов)&lt;/p&gt;
  &lt;p id=&quot;SUJy&quot;&gt;&lt;br /&gt;- Глобальные константы (например, лимиты, дефолтные значения)&lt;/p&gt;
  &lt;p id=&quot;EaZW&quot;&gt;&lt;br /&gt;- Утилиты и хелперы (например, функции для работы с датами, генерация токенов, парсинг переменных окружения)&lt;/p&gt;
  &lt;p id=&quot;AN2o&quot;&gt;&lt;br /&gt;- Глобальные guards, interceptors, pipes&lt;/p&gt;
  &lt;p id=&quot;8gsZ&quot;&gt;&lt;br /&gt;- Базовые технические DTO (например, для health-check)&lt;/p&gt;
  &lt;p id=&quot;dmS3&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;TVKK&quot;&gt; &lt;em&gt;Всё, что касается инфраструктуры, технических аспектов и “склеивания” приложения, должно быть централизовано. Это позволяет быстро вносить изменения, не затрагивая бизнес-логику, и обеспечивает единые стандарты по всему проекту.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;Hrkk&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;02FP&quot;&gt;&lt;strong&gt;Пример:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;// common/filters/http-exception.filter.ts&lt;br /&gt;@Catch(HttpException)&lt;br /&gt;export class HttpExceptionFilter implements ExceptionFilter {&lt;br /&gt;  // ...логика логирования и форматирования ошибок...&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;// common/utils/get-env-var.ts&lt;br /&gt;export function getEnvVar(key: string, fallback?: string): string {&lt;br /&gt;  // ...логика получения переменной окружения с учётом контура...&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;5Ys8&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;yLvx&quot;&gt;&lt;strong&gt;Философия: “Не дублируй, а делись”&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;H7u6&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;g81p&quot;&gt;- Всё, что может понадобиться в нескольких местах — выносится в shared.&lt;br /&gt;- Всё, что обеспечивает техническую инфраструктуру — в common.&lt;br /&gt;- В модулях остаётся только то, что уникально для конкретного бизнес-домена.&lt;/p&gt;
  &lt;p id=&quot;I7j5&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gs2o&quot;&gt;&lt;strong&gt;Преимущества такого подхода:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;oR60&quot;&gt;- &lt;strong&gt;Единые стандарты:&lt;/strong&gt; фронт и бэкенд всегда “говорят на одном языке”.&lt;/p&gt;
  &lt;p id=&quot;uwCn&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Минимум дублирования: &lt;/strong&gt;меньше багов, проще поддержка.&lt;/p&gt;
  &lt;p id=&quot;9bIe&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Быстрый онбординг: &lt;/strong&gt;новому разработчику легко понять, где искать нужный тип или утилиту.&lt;/p&gt;
  &lt;p id=&quot;gh5B&quot;&gt;&lt;br /&gt;- Г&lt;strong&gt;ибкость:&lt;/strong&gt; легко расширять проект, не боясь “сломать” что-то в другом модуле.&lt;/p&gt;
  &lt;p id=&quot;K8y5&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;waAP&quot;&gt;Чёткое разделение shared и common — это не формальность и не вопрос организации файловой системы. Это проявление зрелого инженерного мышления, осознанного подхода к переиспользованию и стратегической заботы о поддерживаемости и будущем продукта.  Придерживаясь этого принципа,  избавляетесь от хаоса и закладываете фундамент прозрачности кода &lt;em&gt;— и ваш код станет чище, а команда — эффективнее.&lt;/em&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;aZuy&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;h2 id=&quot;3LeN&quot;&gt;2. DTO-first API: безопасность, прозрачность, удобство&lt;/h2&gt;
  &lt;p id=&quot;F0Zr&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;qdEB&quot;&gt;2.1. Почему DTO-first?&lt;/h3&gt;
  &lt;p id=&quot;UWke&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;jH6n&quot;&gt;- &lt;strong&gt;Безопасность:&lt;/strong&gt; наружу никогда не уходит entity, только DTO. Даже если в entity появится новое поле (например, “isAdmin”), оно не “утечёт” наружу случайно.&lt;/p&gt;
  &lt;p id=&quot;nmVW&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Явная структура:&lt;/strong&gt; фронтенд и документация всегда знают, что вернёт API. Swagger всегда актуален.&lt;/p&gt;
  &lt;p id=&quot;Ork6&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Гибкость:&lt;/strong&gt; можно легко менять структуру ответа, не трогая базу. Например, добавить computed-поле или убрать лишнее.&lt;/p&gt;
  &lt;p id=&quot;9ywO&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;5I6p&quot;&gt;&lt;strong&gt;2.2. Пример DTO для пользователя&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;6QBV&quot;&gt;&lt;br /&gt;&lt;code&gt;// src/modules/users/dto/user-response.dto.ts&lt;br /&gt;import { ApiProperty } from &amp;#x27;@nestjs/swagger&amp;#x27;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;lMFI&quot;&gt;&lt;code&gt;export class UserResponseDto {&lt;br /&gt;  @ApiProperty({ example: 1 })&lt;br /&gt;  id: number;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;y6IY&quot;&gt;&lt;code&gt;  @ApiProperty({ example: &amp;#x27;user123&amp;#x27; })&lt;br /&gt;  username: string;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;rXJx&quot;&gt;&lt;strong&gt;В сервисе:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;async findAll(): Promise&amp;lt;UserResponseDto[]&amp;gt; {&lt;br /&gt;  const users = await this.usersRepository.find();&lt;br /&gt;  return users.map(({ password, ...user }) =&amp;gt; user as UserResponseDto);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Примечание:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;1w6y&quot;&gt;&lt;em&gt;Пароль никогда не попадёт наружу, даже если кто-то забудет про &lt;code&gt;exclude&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;Q0z3&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;hHhn&quot;&gt;&lt;strong&gt;DTO для параметров пагинации:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;WWrs&quot;&gt;&lt;br /&gt;&lt;code&gt;// src/shared/dto/paginate.dto.ts&lt;br /&gt;import { ApiPropertyOptional } from &amp;#x27;@nestjs/swagger&amp;#x27;;&lt;br /&gt;import { Type } from &amp;#x27;class-transformer&amp;#x27;;&lt;br /&gt;import { IsPositive, Min } from &amp;#x27;class-validator&amp;#x27;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;e2Vt&quot;&gt;&lt;code&gt;export class PaginateDto {&lt;br /&gt;  @ApiPropertyOptional({ example: 1, default: 1 })&lt;br /&gt;  @Type(() =&amp;gt; Number)&lt;br /&gt;  @IsPositive()&lt;br /&gt;  page?: number = 1;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;CUfr&quot;&gt;&lt;code&gt;  @ApiPropertyOptional({ example: 10, default: 10 })&lt;br /&gt;  @Type(() =&amp;gt; Number)&lt;br /&gt;  @Min(1)&lt;br /&gt;  limit?: number = 10;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;q75Z&quot;&gt;&lt;strong&gt;DTO для результата пагинации:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;// src/shared/dto/paginated-result.dto.ts&lt;br /&gt;export interface PaginatedResult&amp;lt;T&amp;gt; {&lt;br /&gt;  items: T[];&lt;br /&gt;  total: number;&lt;br /&gt;  page: number;&lt;br /&gt;  limit: number;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;lgm4&quot;&gt;&lt;strong&gt;Использование в сервисе:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;async findAllPaginated(page = 1, limit = 10): Promise&amp;lt;PaginatedResult&amp;lt;UserResponseDto&amp;gt;&amp;gt; {&lt;br /&gt;  const [items, total] = await this.usersRepository.findAndCount({&lt;br /&gt;    skip: (page - 1) * limit,&lt;br /&gt;    take: limit,&lt;br /&gt;    order: { id: &amp;#x27;ASC&amp;#x27; },&lt;br /&gt;  });&lt;br /&gt;  return {&lt;br /&gt;    items: items.map(({ password, ...user }) =&amp;gt; user as UserResponseDto),&lt;br /&gt;    total,&lt;br /&gt;    page,&lt;br /&gt;    limit,&lt;br /&gt;  };&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;OCIL&quot;&gt;&lt;br /&gt;&lt;strong&gt;Примечание:&lt;br /&gt;&lt;/strong&gt;&lt;em&gt;Пагинация становится стандартной и одинаковой для всех сущностей. Фронт всегда знает, что ожидать.&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;71Y2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;loOH&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Ggkc&quot;&gt;Вот расширенная и более глубокая версия главы о логировании, с примерами, практическими советами и акцентом на инженерную культуру:&lt;/p&gt;
  &lt;p id=&quot;s1P7&quot;&gt;---&lt;/p&gt;
  &lt;h2 id=&quot;KOVY&quot;&gt;3. Логирование: не только для дебага&lt;/h2&gt;
  &lt;p id=&quot;7m0l&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;jwHr&quot;&gt;Логирование — это не типа “выводить что-то в консоль”, чтобы отловить баг. Это полноценный инструмент управления проектом, который помогает видеть невидимое, анализировать поведение системы и принимать обоснованные решения.&lt;/blockquote&gt;
  &lt;p id=&quot;iUrS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;J8gr&quot;&gt;&lt;strong&gt;Почему логирование — это важно&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;upsh&quot;&gt;- &lt;strong&gt;Аудит: &lt;/strong&gt;Логи фиксируют все значимые действия в системе — кто, когда и что сделал. Это важно для расследования инцидентов, анализа истории изменений и соответствия требованиям безопасности.&lt;/p&gt;
  &lt;p id=&quot;g39e&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Поиск и устранение ошибок: &lt;/strong&gt;Хорошо структурированные логи позволяют быстро находить причину сбоя, даже если ошибка проявилась не сразу.&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;Hz7z&quot;&gt;- &lt;strong&gt;Мониторинг и алерты:&lt;/strong&gt; Логи — основа для систем мониторинга (например, Sentry, ELK, Prometheus), которые могут автоматически оповестить команду о критических ошибках или аномалиях.&lt;/p&gt;
  &lt;p id=&quot;yTln&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Аналитика и оптимизация:&lt;/strong&gt; По логам можно анализировать производительность, выявлять узкие места, отслеживать частоту и причины ошибок, строить отчёты по бизнес-событиям.&lt;/p&gt;
  &lt;p id=&quot;u2W4&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;iwuG&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;jLep&quot;&gt;Практика: как мы логируем&lt;/h3&gt;
  &lt;p id=&quot;G8lE&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Vta3&quot;&gt;- Используем встроенный &amp;#x60;Logger&amp;#x60; из &amp;#x60;@nestjs/common&amp;#x60; — он интегрируется с экосистемой NestJS, поддерживает уровни логирования (log, error, warn, debug, verbose) и легко расширяется.&lt;/p&gt;
  &lt;p id=&quot;IfrG&quot;&gt;&lt;br /&gt;- Логируем не только ошибки, но и ключевые бизнес-события: создание пользователя, запуск важной задачи, смена статуса заказа и т.д.&lt;/p&gt;
  &lt;p id=&quot;2WNp&quot;&gt;&lt;br /&gt;- В фильтрах ошибок обязательно логируем stack trace — это ускоряет диагностику и помогает понять, где именно произошёл сбой.&lt;/p&gt;
  &lt;p id=&quot;KFWn&quot;&gt;&lt;br /&gt;- Для сложных сценариев используем контекст (context) — чтобы в логах всегда было понятно, из какого модуля или сервиса пришло сообщение.&lt;/p&gt;
  &lt;p id=&quot;9sQg&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;9u8S&quot;&gt;&lt;strong&gt;Пример:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;import { Logger } from &amp;#x27;@nestjs/common&amp;#x27;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;YLdz&quot;&gt;&lt;code&gt;@Injectable()&lt;br /&gt;export class UsersService {&lt;br /&gt;  private readonly logger = new Logger(UsersService.name);&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;g4aU&quot;&gt;&lt;code&gt;  async create(username: string, password: string): Promise&amp;lt;UserResponseDto&amp;gt; {&lt;br /&gt;    this.logger.log(&amp;#x60;Создание пользователя: ${username}&amp;#x60;);&lt;br /&gt;    try {&lt;br /&gt;      // ... логика создания пользователя ...&lt;br /&gt;      this.logger.log(&amp;#x60;Пользователь успешно создан: ${username}&amp;#x60;);&lt;br /&gt;    } catch (error) {&lt;br /&gt;      this.logger.error(&amp;#x60;Ошибка при создании пользователя: ${username}&amp;#x60;, error.stack);&lt;br /&gt;      throw error;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;S1YX&quot;&gt;&lt;strong&gt;Советы и best practices&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;geZn&quot;&gt;- &lt;strong&gt;Стандартизируйте формат логов:&lt;/strong&gt; Используйте единый стиль сообщений, чтобы их было легко парсить и анализировать.&lt;/p&gt;
  &lt;p id=&quot;nGIX&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Не логируйте чувствительные данные:&lt;/strong&gt; Никогда не пишите в логи пароли, токены, персональные данные пользователей.&lt;/p&gt;
  &lt;p id=&quot;BOmQ&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Используйте уровни логирования:&lt;/strong&gt; Для обычных событий — &amp;#x60;log&amp;#x60;, для подозрительных — &amp;#x60;warn&amp;#x60;, для ошибок — &amp;#x60;error&amp;#x60;, для отладки — &amp;#x60;debug&amp;#x60;.&lt;/p&gt;
  &lt;p id=&quot;Au3I&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Интегрируйте логи с внешними системами: &lt;/strong&gt;Используйте централизованные системы сбора логов (например, ELK, Graylog, Sentry), чтобы не терять важную информацию и быстро реагировать на инциденты.&lt;/p&gt;
  &lt;p id=&quot;iy0Y&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Добавляйте контекст: &lt;/strong&gt;Указывайте, из какого модуля, сервиса или запроса пришёл лог — это ускоряет поиск нужной информации.&lt;/p&gt;
  &lt;p id=&quot;aJLA&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;Ddrd&quot;&gt;&lt;strong&gt;Логирование&lt;/strong&gt; —  не “дополнительная опция”, а неотъемлемая часть зрелого backend-проекта.  Грамотно выстроенная система логов превращает хаос в управляемую систему, помогает команде быстрее реагировать на проблемы, анализировать поведение пользователей и строить действительно надёжные сервисы.  &lt;/blockquote&gt;
  &lt;p id=&quot;gOp6&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;HL1z&quot;&gt;&lt;strong&gt;Инвестируйте время в логи — и они не раз спасут ваш проект.&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;SPGJ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;EPPq&quot;&gt;Вот более развернутая, строгая и профессионально оформленная версия раздела о валидации и обработке ошибок, с акцентом на инженерную культуру и безопасность:&lt;/p&gt;
  &lt;p id=&quot;zVGL&quot;&gt;---&lt;/p&gt;
  &lt;h2 id=&quot;3fHt&quot;&gt;4. Валидация и обработка ошибок: доверяй, но проверяй&lt;/h2&gt;
  &lt;p id=&quot;TPam&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;luiH&quot;&gt;В современных backend-системах нет места случайностям: каждый входящий запрос проходит строгую валидацию, а любая ошибка обрабатывается так, чтобы обеспечить безопасность пользователя и стабильность всей платформы.&lt;/blockquote&gt;
  &lt;p id=&quot;Y9zR&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;roO1&quot;&gt;- &lt;strong&gt;Валидация данных: &lt;/strong&gt;Для всех входных DTO мы используем возможности &amp;#x60;class-validator&amp;#x60;. Это гарантирует, что на уровень бизнес-логики никогда не попадут “грязные” или некорректные данные. Валидация происходит автоматически, а правила описываются декларативно прямо в классах DTO.&lt;/p&gt;
  &lt;p id=&quot;ikxK&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;uoQY&quot;&gt;&lt;strong&gt;Пример DTO с валидацией:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;import { IsString, MinLength } from &amp;#x27;class-validator&amp;#x27;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;cLGf&quot;&gt;&lt;code&gt;export class CreateUserDto {&lt;br /&gt;  @IsString()&lt;br /&gt;  username: string;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;XubA&quot;&gt;&lt;code&gt;  @IsString()&lt;br /&gt;  @MinLength(8)&lt;br /&gt;  password: string;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Такой подход устраняет ошибки на входе  и превращает API в динамично проверяемый и самоописательный интерфейс,  правила валидации становятся частью кода и документации одновременно. Это позволяет команде быстро внедрять изменения, интегрироваться без лишних согласований и строить надёжные сервисы с минимальными коммуникационными издержками.&lt;/p&gt;
  &lt;p id=&quot;Ly2O&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;9CHS&quot;&gt;&lt;strong&gt;Обработка ошибок: &lt;/strong&gt;Все ошибки, возникающие в приложении, перехватываются глобальным фильтром. &lt;/p&gt;
  &lt;p id=&quot;Iz4f&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;W6LJ&quot;&gt;&lt;strong&gt;Мы реализуем собственный фильтр (&amp;#x60;HttpExceptionFilter&amp;#x60;), который:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;cRhx&quot;&gt;&lt;br /&gt;  - Логирует ошибку и stack trace для последующего анализа.&lt;/p&gt;
  &lt;p id=&quot;pmBn&quot;&gt;&lt;br /&gt;  - Формирует структурированный и предсказуемый ответ для клиента.&lt;/p&gt;
  &lt;p id=&quot;5XYQ&quot;&gt;&lt;br /&gt;  - Никогда не раскрывает внутренние детали реализации, стек вызовов или чувствительную информацию наружу.&lt;/p&gt;
  &lt;p id=&quot;boMo&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;99AP&quot;&gt;&lt;strong&gt;Пример глобального фильтра ошибок:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;@Catch(HttpException)&lt;br /&gt;export class HttpExceptionFilter implements ExceptionFilter {&lt;br /&gt;  private readonly logger = new Logger(HttpExceptionFilter.name);&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;ToO4&quot;&gt;&lt;code&gt;  catch(exception: HttpException, host: ArgumentsHost) {&lt;br /&gt;    // Логируем ошибку для внутреннего аудита&lt;br /&gt;    this.logger.error(exception.message, exception.stack);&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;iRIT&quot;&gt;&lt;code&gt;    // Формируем безопасный и понятный ответ для клиента&lt;br /&gt;    const ctx = host.switchToHttp();&lt;br /&gt;    const response = ctx.getResponse&amp;lt;Response&amp;gt;();&lt;br /&gt;    const status = exception.getStatus();&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;bWvi&quot;&gt;&lt;code&gt;    response.status(status).json({&lt;br /&gt;      statusCode: status,&lt;br /&gt;      message: exception.message,&lt;br /&gt;      // Можно добавить поле errors для передачи детальной информации о валидации&lt;br /&gt;    });&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;Yp8D&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;z2vI&quot;&gt;&lt;strong&gt;Почему это важно:  &lt;/strong&gt;&lt;br /&gt;Пользователь всегда получает информативный, но безопасный ответ — без “500 Internal Server Error” и загадочных сообщений. Внутри команды мы сохраняем всю необходимую информацию для диагностики и исправления ошибок, не подвергая риску безопасность или приватность данных.&lt;/p&gt;
  &lt;blockquote id=&quot;N6TG&quot;&gt;  Жёсткая валидация и централизованная обработка ошибок — это не просто “best practice”, а фундамент надёжности, безопасности и предсказуемости вашего backend-приложения.&lt;/blockquote&gt;
  &lt;p id=&quot;jc8R&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;4Deu&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;HnOt&quot;&gt; 5. Swagger-документация: всегда актуальна&lt;/h2&gt;
  &lt;p id=&quot;MUt3&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;3UPF&quot;&gt;Зачем нам это нужно?&lt;/h3&gt;
  &lt;p id=&quot;WOv9&quot;&gt;Представьте: новый разработчик приходит в команду и ему нужно разобраться с API. Или клиент хочет интегрироваться с вашим сервисом. Что они видят? Если это куча непонятных эндпоинтов без объяснений - это путь к бесконечным вопросам и ошибкам.&lt;/p&gt;
  &lt;p id=&quot;ERH9&quot;&gt;Swagger - это не  документация, это ваш способ общения с внешним миром. И как любое общение, оно должно быть понятным и приятным.&lt;/p&gt;
  &lt;p id=&quot;aquk&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;pyF0&quot;&gt;&lt;strong&gt;Основные принципы&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;IEZh&quot;&gt;
    &lt;li id=&quot;Kvg4&quot;&gt;Полная документация: Каждый эндпоинт, DTO и модель должны быть полностью документированы с помощью Swagger-аннотаций&lt;/li&gt;
    &lt;li id=&quot;kThe&quot;&gt;Автоматическая валидация: Документация проверяется автоматически при сборке проекта&lt;/li&gt;
    &lt;li id=&quot;oRz0&quot;&gt;Интерактивность: Swagger UI предоставляет возможность тестирования API прямо из браузера&lt;/li&gt;
    &lt;li id=&quot;ViUZ&quot;&gt;Версионирование: Поддержка версионирования API с отдельной документацией для каждой версии&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;oK22&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Cq2m&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;7olR&quot;&gt;&lt;strong&gt;Как мы это делаем?&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;W2Jl&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;CVdy&quot;&gt;&lt;strong&gt;1. Пишем документацию как код&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;m236&quot;&gt;Каждый &lt;strong&gt;DTO&lt;/strong&gt; -  не просто класс, это история о том, что он делает:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;code&gt;@ApiProperty({&lt;br /&gt;  description: &amp;#x27;Идентификатор пользователя в системе&amp;#x27;,&lt;br /&gt;  example: &amp;#x27;507f1f77bcf86cd799439011&amp;#x27;,&lt;br /&gt;  required: true&lt;br /&gt;})&lt;br /&gt;userId: string;&lt;/code&gt;&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;hmPV&quot;&gt;Каждый &lt;strong&gt;эндпоинт&lt;/strong&gt; -  не просто метод, это инструкция:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;@ApiOperation({&lt;br /&gt;  summary: &amp;#x27;Создание нового пользователя&amp;#x27;,&lt;br /&gt;  description: &amp;#x27;Регистрирует нового пользователя в системе. Требуется подтверждение email.&amp;#x27;&lt;br /&gt;})&lt;br /&gt;@ApiResponse({&lt;br /&gt;  status: 201,&lt;br /&gt;  description: &amp;#x27;Пользователь успешно создан&amp;#x27;,&lt;br /&gt;  type: UserResponseDto&lt;br /&gt;})&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;UpBy&quot;&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;5HuG&quot;&gt;&lt;strong&gt;2. Делаем документацию живой&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;mrMn&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;nW0O&quot;&gt;- &lt;strong&gt;Примеры из реальной жизни:&lt;/strong&gt; вместо абстрактных значений показываем реальные данные&lt;/p&gt;
  &lt;p id=&quot;4xgf&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Ошибки и их решения:&lt;/strong&gt; описываем успешные сценарии и что делать, если что-то пошло не так&lt;/p&gt;
  &lt;p id=&quot;Dr3d&quot;&gt;&lt;br /&gt;- &lt;strong&gt;Секьюрность: &lt;/strong&gt;объясняем, как работать с авторизацией, какие токены нужны и где их брать&lt;/p&gt;
  &lt;p id=&quot;uHES&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;XVOB&quot;&gt;&lt;strong&gt;3. Организуем информацию&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;hMRh&quot;&gt;Представьте, что вы заходите в библиотеку. Книги разложены по полкам, есть указатели, каталоги. Так же и с API:&lt;/p&gt;
  &lt;p id=&quot;Ry1M&quot;&gt;- Группируем эндпоинты по смыслу (пользователи, платежи, отчеты)&lt;br /&gt;- Добавляем теги для быстрой навигации&lt;br /&gt;- Создаем иерархию: от общего к частному&lt;/p&gt;
  &lt;p id=&quot;dOdt&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;yZYK&quot;&gt;&lt;strong&gt;4. Автоматизируем процесс&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;iGMB&quot;&gt;Документация - это не разовая акция, а постоянный процесс:&lt;/p&gt;
  &lt;p id=&quot;iOe8&quot;&gt;- В CI/CD проверяем, что документация соответствует коду&lt;br /&gt;- При мердж-реквесте автоматически проверяем наличие описаний&lt;br /&gt;- Генерируем клиентские SDK на основе документации&lt;/p&gt;
  &lt;p id=&quot;sWBH&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;grPL&quot;&gt;Что получаем в итоге?&lt;/h3&gt;
  &lt;p id=&quot;6tOS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;iIAW&quot;&gt;1. &lt;strong&gt;Для команды:&lt;/strong&gt;&lt;br /&gt;   - Меньше вопросов &amp;quot;а как это работает?&amp;quot;&lt;br /&gt;   - Быстрее ввод новых разработчиков&lt;br /&gt;   - Меньше ошибок при интеграции&lt;br /&gt;   - Понятный интерфейс для тестирования API&lt;br /&gt;   - Готовые примеры кода&lt;br /&gt;   - Четкое понимание ограничений и возможностей&lt;/p&gt;
  &lt;p id=&quot;YNf8&quot;&gt;2. Для бизнеса:&lt;br /&gt;   - Меньше времени на поддержку&lt;br /&gt;   - Более быстрая интеграция партнеров&lt;br /&gt;   - Профессиональный имидж&lt;/p&gt;
  &lt;p id=&quot;4Emi&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;FSh0&quot;&gt;Советы из практики&lt;/h3&gt;
  &lt;p id=&quot;oper&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;wjCo&quot;&gt;1. &lt;strong&gt;Пишите для людей:&lt;/strong&gt;&lt;br /&gt;   - Используйте простой язык&lt;br /&gt;   - Добавляйте примеры из реальной жизни&lt;br /&gt;   - Объясняйте неочевидные моменты&lt;/p&gt;
  &lt;p id=&quot;MWlo&quot;&gt;2. &lt;strong&gt;Держите в актуальном состоянии:&lt;/strong&gt;&lt;br /&gt;   - Сделайте обновление документации частью процесса разработки&lt;br /&gt;   - Проверяйте актуальность при каждом изменении API&lt;br /&gt;   - Собирайте обратную связь от пользователей&lt;/p&gt;
  &lt;p id=&quot;ZWlv&quot;&gt;3. &lt;strong&gt;Делайте красиво:&lt;/strong&gt;&lt;br /&gt;   - Добавьте логотип и фирменные цвета&lt;br /&gt;   - Структурируйте информацию логично&lt;br /&gt;   - Используйте форматирование для лучшей читаемости&lt;/p&gt;
  &lt;p id=&quot;LUbk&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;vBP5&quot;&gt;Важный совет:&lt;/h3&gt;
  &lt;p id=&quot;OKuy&quot;&gt;Хорошая документация - это как хороший гид в незнакомом городе. Каждый перекресток (интеграция) снабжен понятными указателями, а все подводные камни (ограничения и особенности) заранее отмечены на карте. Хотите превратить вашу документацию из скучного справочника в увлекательный путеводитель? Стремитесь создавать документацию, которая не просто объясняет, а вдохновляет разработчиков на использование вашего API!&lt;/p&gt;
  &lt;p id=&quot;Eg1W&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;ltJA&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;haF1&quot;&gt;6. Безопасность: не доверяй даже себе&lt;/h2&gt;
  &lt;p id=&quot;jYys&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;8U6V&quot;&gt;В современной разработке безопасность - это философия, которая должна пронизывать каждый аспект разработки. Давайте поговорим о том, как мы подходим к этому вопросу в нашем проекте.&lt;/p&gt;
  &lt;p id=&quot;EA2P&quot;&gt;Основной принцип, которым мы руководствуемся - это принцип минимальных привилегий. Каждый компонент системы должен иметь доступ только к тем ресурсам, которые ему действительно необходимы для работы. Это касается как кода, так и инфраструктуры.&lt;/p&gt;
  &lt;p id=&quot;8nCo&quot;&gt;Имея дело с  аутентификацией,  используй JWT с умом. Токены имеют ограниченное время жизни, регулярно обновляются, а их секреты хранятся в защищённом окружении. Но самое главное - мы никогда не доверяем клиенту полностью. Каждый запрос проходит через строгую валидацию и проверку прав доступа. Работа с паролями - это отдельная история. Мы хешируем их перед сохранением и используем современные алгоритмы с солью и итерациями. &lt;/p&gt;
  &lt;p id=&quot;Fkhe&quot;&gt;Все секреты, ключи и конфигурации живут только в .env файлах, которые никогда не попадают в репозиторий. Для разработчиков мы предоставляем .env.example с безопасными тестовыми значениями.&lt;/p&gt;
  &lt;p id=&quot;6Kes&quot;&gt;Но безопасность - это не только про код. Это про процессы и культуру разработки. Каждый пулл-реквест проходит проверку на безопасность, мы регулярно обновляем зависимости и проводим аудиты. Наши разработчики обучены распознавать потенциальные уязвимости и знают, как их избежать.&lt;/p&gt;
  &lt;p id=&quot;j3xz&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;zdYL&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;jGvH&quot;&gt;&lt;strong&gt;Хранение паролей: &lt;/strong&gt;только хеши, никогда исходные значения&lt;/p&gt;
  &lt;p id=&quot;ZrFu&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gUAY&quot;&gt; Давайте разберем, как это работает на практике:&lt;/p&gt;
  &lt;p id=&quot;OjvQ&quot;&gt;&lt;br /&gt;&lt;code&gt;@Injectable()&lt;br /&gt;export class PasswordService {&lt;br /&gt;  private readonly SALT_ROUNDS = 12;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;yi4p&quot;&gt;&lt;code&gt;  async hashPassword(password: string): Promise&amp;lt;string&amp;gt; {&lt;br /&gt;    // Генерируем уникальную соль для каждого пароля&lt;br /&gt;    const salt = await bcrypt.genSalt(this.SALT_ROUNDS);&lt;br /&gt;    // Хешируем пароль с солью&lt;br /&gt;    return bcrypt.hash(password, salt);&lt;br /&gt;  }&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;YEgD&quot;&gt;&lt;code&gt;  async verifyPassword(password: string, hash: string): Promise&amp;lt;boolean&amp;gt; {&lt;br /&gt;    // Сравниваем введенный пароль с хешем из базы&lt;br /&gt;    return bcrypt.compare(password, hash);&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;k73u&quot;&gt;Когда пользователь регистрируется, мы сразу хешируем его пароль:&lt;/p&gt;
  &lt;p id=&quot;V8PC&quot;&gt;&lt;br /&gt;&lt;code&gt;@Injectable()&lt;br /&gt;export class AuthService {&lt;br /&gt;  async register(createUserDto: CreateUserDto) {&lt;br /&gt;    const hashedPassword = await this.passwordService.hashPassword(createUserDto.password);&lt;br /&gt;    &lt;br /&gt;    const user = await this.userRepository.create({&lt;br /&gt;      ...createUserDto,&lt;br /&gt;      password: hashedPassword // Сохраняем только хеш&lt;br /&gt;    });&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;ncb5&quot;&gt;&lt;code&gt;    // Никогда не возвращаем пароль, даже хешированный&lt;br /&gt;    const { password, ...safeUser } = user;&lt;br /&gt;    return safeUser;&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;PtXv&quot;&gt;Этот подход обеспечивает несколько уровней защиты:&lt;/p&gt;
  &lt;p id=&quot;Ibta&quot;&gt;1. Даже если злоумышленник получит доступ к базе данных, он не сможет восстановить исходные пароли&lt;/p&gt;
  &lt;p id=&quot;hZj2&quot;&gt;&lt;br /&gt;2. Использование соли предотвращает атаки с использованием радужных таблиц&lt;/p&gt;
  &lt;p id=&quot;dXH9&quot;&gt;&lt;br /&gt;3. Современные алгоритмы хеширования: &lt;/p&gt;
  &lt;p id=&quot;VnSd&quot;&gt;&lt;strong&gt;Argon2&lt;/strong&gt; - победитель Password Hashing Competition 2015, предлагающий три варианта:&lt;/p&gt;
  &lt;ul id=&quot;8hRf&quot;&gt;
    &lt;li id=&quot;sogk&quot;&gt;Argon2d - максимизирует устойчивость к GPU-атакам&lt;/li&gt;
    &lt;li id=&quot;hlgu&quot;&gt;Argon2i - оптимизирован для защиты от side-channel атак&lt;/li&gt;
    &lt;li id=&quot;Xu17&quot;&gt;Argon2id - гибридный подход, сочетающий преимущества обоих вариантов&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;lfcm&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;xf0L&quot;&gt;&lt;strong&gt;scrypt&lt;/strong&gt; - алгоритм, специально разработанный для противодействия аппаратным атакам, требующий большого объема памяти для вычислений.&lt;/p&gt;
  &lt;p id=&quot;hMkn&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;9vNK&quot;&gt;&lt;strong&gt;PBKDF2&lt;/strong&gt; (Password-Based Key Derivation Function 2) - широко используемый стандарт, поддерживающий различные хеш-функции и итерации.&lt;/p&gt;
  &lt;p id=&quot;wxxr&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;PzJd&quot;&gt;&lt;strong&gt;Balloon Hashing&lt;/strong&gt; - современный алгоритм, использующий псевдослучайные перестановки памяти для создания &amp;quot;вычислительного барьера&amp;quot;.&lt;/p&gt;
  &lt;p id=&quot;AA9e&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;OhBI&quot;&gt;Важно отметить, что мы не только не храним пароли, но и не логируем их ни в каком виде. Даже в случае ошибок аутентификации мы записываем только факт попытки, без каких-либо деталей о введенных данных.&lt;/p&gt;
  &lt;p id=&quot;szr8&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;8du6&quot;&gt;&lt;strong&gt;При аутентификации мы сравниваем хеш введенного пароля с хешем в базе:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;KcEX&quot;&gt;&lt;code&gt;@Injectable()&lt;br /&gt;export class AuthService {&lt;br /&gt;  async hashPassword(password: string): Promise&amp;lt;string&amp;gt; {&lt;br /&gt;    const salt = await bcrypt.genSalt(12);&lt;br /&gt;    return bcrypt.hash(password, salt);&lt;br /&gt;  }&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;NvtW&quot;&gt;&lt;code&gt;  async validateUser(email: string, password: string) {&lt;br /&gt;    const user = await this.usersService.findByEmail(email);&lt;br /&gt;    if (!user) return null;&lt;br /&gt;    &lt;br /&gt;    const isValid = await Argon2.verify(user.password, pass);&lt;br /&gt;    if (!isValid) return null;&lt;br /&gt;    &lt;br /&gt;    // Никогда не возвращаем пароль&lt;br /&gt;    const { password: _, ...safeUser } = user;&lt;br /&gt;    return safeUser;&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;Tkao&quot;&gt;Этот подход к хранению паролей является отраслевым стандартом и обеспечивает надежную защиту данных пользователей даже в случае компрометации базы данных.&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;hEP1&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;0Kso&quot;&gt;## Защита эндпоинтов&lt;/p&gt;
  &lt;p id=&quot;fx1j&quot;&gt;Защищенные маршруты используют кастомные guards и декораторы:&lt;/p&gt;
  &lt;p id=&quot;k4gt&quot;&gt;&lt;br /&gt;&lt;code&gt;@UseGuards(JwtAuthGuard, RolesGuard)&lt;br /&gt;@Roles(UserRole.ADMIN)&lt;br /&gt;@Controller(&amp;#x27;admin&amp;#x27;)&lt;br /&gt;export class AdminController {&lt;br /&gt;  @Get(&amp;#x27;users&amp;#x27;)&lt;br /&gt;  async getUsers() {&lt;br /&gt;    // Только админы могут получить доступ&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;pAlq&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;OQr8&quot;&gt;&lt;strong&gt; Практические рекомендации:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;FcWj&quot;&gt;1. &lt;strong&gt;Работа с секретами&lt;/strong&gt;&lt;br /&gt;   - Используйте &amp;#x60;.env&amp;#x60; для всех конфиденциальных данных&lt;br /&gt;   - Регулярно ротируйте ключи и пароли&lt;br /&gt;   - Храните разные секреты для разных окружений&lt;/p&gt;
  &lt;p id=&quot;Nd0u&quot;&gt;2. &lt;strong&gt;Защита от атак&lt;/strong&gt;&lt;br /&gt;   - Внедрите rate limiting&lt;br /&gt;   - Настройте CORS политики&lt;br /&gt;   - Используйте helmet для HTTP-заголовков&lt;/p&gt;
  &lt;p id=&quot;JT8n&quot;&gt;3. &lt;strong&gt;Процессы разработки&lt;/strong&gt;&lt;br /&gt;   - Включайте проверку безопасности в CI/CD&lt;br /&gt;   - Проводите регулярные аудиты кода&lt;br /&gt;   - Обучайте команду основам безопасностиё&lt;/p&gt;
  &lt;p id=&quot;T5di&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;VjPL&quot;&gt;Реальный пример защиты API:&lt;/h3&gt;
  &lt;p id=&quot;QWcw&quot;&gt;&lt;br /&gt;&lt;code&gt;@Controller(&amp;#x27;api/v1&amp;#x27;)&lt;br /&gt;export class SecureController {&lt;br /&gt;  @UseGuards(JwtAuthGuard)&lt;br /&gt;  @UseInterceptors(ClassSerializerInterceptor)&lt;br /&gt;  @Get(&amp;#x27;user/profile&amp;#x27;)&lt;br /&gt;  async getProfile(@CurrentUser() user: User) {&lt;br /&gt;    // Сериализуем только безопасные поля&lt;br /&gt;    return new UserResponseDto(user);&lt;br /&gt;  }&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;0mFy&quot;&gt;&lt;code&gt;  @UseGuards(RolesGuard)&lt;br /&gt;  @Roles(UserRole.ADMIN)&lt;br /&gt;  @Post(&amp;#x27;admin/users&amp;#x27;)&lt;br /&gt;  async createUser(@Body() createUserDto: CreateUserDto) {&lt;br /&gt;    // Проверяем права и валидируем данные&lt;br /&gt;    return this.userService.create(createUserDto);&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;iO8F&quot;&gt;Безопасность - это постоянный процесс улучшения и адаптации. не достаточно просто следовать лучшим практикам, следует  развивать их и адаптировать под реальные нужды. Каждый день мы учимся чему-то новому и применяем эти знания для защиты наших систем и данных пользователей.&lt;/blockquote&gt;
  &lt;p id=&quot;LIjs&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;qU4i&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Dxth&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;dCEE&quot;&gt;7. Тесты: не только для CI&lt;/h2&gt;
  &lt;p id=&quot;UtHr&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;QbN2&quot;&gt;В современной разработке тестирование эволюционировало из простой проверки кода в мощный инструмент обеспечения качества и надежности системы. Давайте посмотрим, как мы реализуем современный подход к тестированию.&lt;/p&gt;
  &lt;p id=&quot;w7lS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;qMmt&quot;&gt;- Для всех сервисов и контроллеров пишем unit- и e2e-тесты.&lt;br /&gt;- Тесты проверяют не только happy path, но и ошибки, edge cases.&lt;br /&gt;- Покрытие тестами — не самоцель, а инструмент уверенности в коде.&lt;/p&gt;
  &lt;p id=&quot;WZqF&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Mw7W&quot;&gt;Пример современного подхода  к написанию тестов:&lt;/p&gt;
  &lt;p id=&quot;J4CN&quot;&gt;&lt;br /&gt;&lt;code&gt;import { Test, TestingModule } from &amp;#x27;@nestjs/testing&amp;#x27;;&lt;br /&gt;import { faker } from &amp;#x27;@faker-js/faker&amp;#x27;;&lt;br /&gt;import { PrismaService } from &amp;#x27;@prisma/client&amp;#x27;;&lt;br /&gt;import { UserService } from &amp;#x27;./user.service&amp;#x27;;&lt;br /&gt;import { createMock } from &amp;#x27;@golevelup/ts-jest&amp;#x27;;&lt;br /&gt;import { mockDeep } from &amp;#x27;jest-mock-extended&amp;#x27;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;VweI&quot;&gt;&lt;code&gt;describe(&amp;#x27;UserService&amp;#x27;, () =&amp;gt; {&lt;br /&gt;  let service: UserService;&lt;br /&gt;  let prisma: DeepMockProxy&amp;lt;PrismaService&amp;gt;;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;MyOf&quot;&gt;&lt;code&gt;  beforeEach(async () =&amp;gt; {&lt;br /&gt;    prisma = mockDeep&amp;lt;PrismaService&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    const module: TestingModule = await Test.createTestingModule({&lt;br /&gt;      providers: [&lt;br /&gt;        UserService,&lt;br /&gt;        {&lt;br /&gt;          provide: PrismaService,&lt;br /&gt;          useValue: prisma,&lt;br /&gt;        },&lt;br /&gt;      ],&lt;br /&gt;    }).compile();&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;BlLg&quot;&gt;&lt;code&gt;    service = module.get(UserService);&lt;br /&gt;  });&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;JiUk&quot;&gt;&lt;code&gt;  it(&amp;#x27;should create user with valid data&amp;#x27;, async () =&amp;gt; {&lt;br /&gt;    const userData = {&lt;br /&gt;      email: faker.internet.email(),&lt;br /&gt;      password: faker.internet.password({ length: 12 }),&lt;br /&gt;    };&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;R30G&quot;&gt;&lt;code&gt;    prisma.user.create.mockResolvedValue({&lt;br /&gt;      id: faker.string.uuid(),&lt;br /&gt;      ...userData,&lt;br /&gt;      createdAt: faker.date.past(),&lt;br /&gt;      updatedAt: faker.date.recent(),&lt;br /&gt;    });&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;jdzh&quot;&gt;&lt;code&gt;    const result = await service.create(userData);&lt;br /&gt;    &lt;br /&gt;    expect(result).toMatchObject({&lt;br /&gt;      email: userData.email,&lt;br /&gt;    });&lt;br /&gt;    expect(result.password).toBeUndefined();&lt;br /&gt;  });&lt;br /&gt;});&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;034w&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;BL52&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;sHI0&quot;&gt;&lt;strong&gt;Интеграция с  CI/CD&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;Z1r9&quot;&gt;&lt;br /&gt;&lt;code&gt;# .github/workflows/test.yml&lt;br /&gt;name: Modern Test Pipeline&lt;br /&gt;on: [push, pull_request]&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;ir93&quot;&gt;&lt;code&gt;jobs:&lt;br /&gt;  test:&lt;br /&gt;    runs-on: ubuntu-latest&lt;br /&gt;    strategy:&lt;br /&gt;      matrix:&lt;br /&gt;        node-version: [18.x, 20.x]&lt;br /&gt;    steps:&lt;br /&gt;      - uses: actions/checkout@v3&lt;br /&gt;      - name: Use Node.js ${{ matrix.node-version }}&lt;br /&gt;        uses: actions/setup-node@v3&lt;br /&gt;        with:&lt;br /&gt;          node-version: ${{ matrix.node-version }}&lt;br /&gt;          cache: &amp;#x27;npm&amp;#x27;&lt;br /&gt;      &lt;br /&gt;      - name: Install dependencies&lt;br /&gt;        run: npm ci&lt;br /&gt;        &lt;br /&gt;      - name: Run unit tests&lt;br /&gt;        run: npm run test:unit&lt;br /&gt;        env:&lt;br /&gt;          NODE_ENV: test&lt;br /&gt;          &lt;br /&gt;      - name: Run e2e tests&lt;br /&gt;        run: npm run test:e2e&lt;br /&gt;        env:&lt;br /&gt;          NODE_ENV: test&lt;br /&gt;          TEST_DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}&lt;br /&gt;          &lt;br /&gt;      - name: Run mutation tests&lt;br /&gt;        run: npm run test:mutation&lt;br /&gt;        &lt;br /&gt;      - name: Upload coverage&lt;br /&gt;        uses: codecov/codecov-action@v3&lt;br /&gt;        with:&lt;br /&gt;          token: ${{ secrets.CODECOV_TOKEN }}&lt;br /&gt;          &lt;br /&gt;      - name: Performance test&lt;br /&gt;        run: npm run test:performance&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;jkXT&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;pvIV&quot;&gt;Современное тестирование - это комплексный подход к обеспечению качества, включающий:&lt;br /&gt;- Автоматизированное тестирование на разных уровнях&lt;br /&gt;- Мониторинг производительности&lt;br /&gt;- Проверку контрактов API&lt;br /&gt;- Тестиование мутаций &lt;br /&gt;- Интеграцию с современными инструментами CI/CD&lt;/p&gt;
  &lt;p id=&quot;x5Ry&quot;&gt;Это позволяет  находить ошибки и предотвращать их появление, обеспечивая высокое качество кода и надежность системы.&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;3uNS&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;S4qi&quot;&gt;8. Docker и окружение: dev ≠ prod&lt;/h2&gt;
  &lt;p id=&quot;6xne&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;hwOa&quot;&gt;8.1. Три контура — dev, stage, prod&lt;/h3&gt;
  &lt;p id=&quot;sUhi&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Q7qJ&quot;&gt;&lt;strong&gt;Используем default + три docker-compose файла:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;3pmH&quot;&gt;&lt;br /&gt;  &lt;strong&gt;docker-compose.yml&lt;/strong&gt; — базовый, описывает сервисы.&lt;/p&gt;
  &lt;p id=&quot;eKgJ&quot;&gt;&lt;br /&gt;  &lt;strong&gt;docker-compose.dev.yml&lt;/strong&gt; — dev-override: монтирует исходники, пробрасывает порты, использует dev-окружение.&lt;/p&gt;
  &lt;p id=&quot;JgGy&quot;&gt;&lt;br /&gt;  &lt;strong&gt;docker-compose.stage.yml&lt;/strong&gt; и &amp;#x60;docker-compose.prod.yml&amp;#x60; — для stage и prod, с разными портами, переменными, настройками безопасности.&lt;/p&gt;
  &lt;p id=&quot;LCEU&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;GzAR&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;ktji&quot;&gt;&lt;strong&gt;Пример:&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;# docker-compose.yml&lt;br /&gt;services:&lt;br /&gt;  app:&lt;br /&gt;    build:&lt;br /&gt;      context: .&lt;br /&gt;      dockerfile: Dockerfile&lt;br /&gt;    depends_on:&lt;br /&gt;      - db&lt;br /&gt;    networks:&lt;br /&gt;      - gprod-network&lt;br /&gt;  db:&lt;br /&gt;    image: postgres:13&lt;br /&gt;    volumes:&lt;br /&gt;      - pgdata:/var/lib/postgresql/data&lt;br /&gt;    networks:&lt;br /&gt;      - gprod-network&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;Amry&quot;&gt;&lt;code&gt;volumes:&lt;br /&gt;  pgdata:&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;GRaZ&quot;&gt;&lt;code&gt;networks:&lt;br /&gt;  gprod-network:&lt;br /&gt;    driver: bridge&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;8ie4&quot;&gt;&lt;br /&gt;&lt;code&gt;# docker-compose.dev.yml&lt;br /&gt;services:&lt;br /&gt;  app:&lt;br /&gt;    env_file:&lt;br /&gt;      - .env&lt;br /&gt;    environment:&lt;br /&gt;      - NODE_ENV=development&lt;br /&gt;      - DATABASE_HOST=db&lt;br /&gt;      - DATABASE_PORT=5432&lt;br /&gt;      - DATABASE_USER=${DEV_DATABASE_USER}&lt;br /&gt;      - DATABASE_PASSWORD=${DEV_DATABASE_PASSWORD}&lt;br /&gt;      - DATABASE_NAME=${DEV_DATABASE_NAME}&lt;br /&gt;      - JWT_SECRET=${DEV_JWT_SECRET}&lt;br /&gt;    ports:&lt;br /&gt;      - &amp;quot;3000:3000&amp;quot;&lt;br /&gt;    volumes:&lt;br /&gt;      - .:/app&lt;br /&gt;      - /app/node_modules&lt;br /&gt;    depends_on:&lt;br /&gt;      - db&lt;br /&gt;    networks:&lt;br /&gt;      - gprod-network&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;vQUE&quot;&gt;&lt;code&gt;  db:&lt;br /&gt;    image: postgres:13&lt;br /&gt;    environment:&lt;br /&gt;      - POSTGRES_USER=${DEV_DATABASE_USER}&lt;br /&gt;      - POSTGRES_PASSWORD=${DEV_DATABASE_PASSWORD}&lt;br /&gt;      - POSTGRES_DB=${DEV_DATABASE_NAME}&lt;br /&gt;    ports:&lt;br /&gt;      - &amp;quot;5432:5432&amp;quot;&lt;br /&gt;    networks:&lt;br /&gt;      - gprod-network&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;nyP0&quot;&gt;&lt;strong&gt;Пояснение: &lt;br /&gt;&lt;/strong&gt;- Для каждого окружения свои переменные, свои порты, свои тома.&lt;br /&gt;- В dev-контуре код монтируется внутрь контейнера для hot-reload.&lt;br /&gt;- В prod — только собранный dist, никакого доступа к исходникам.&lt;/p&gt;
  &lt;p id=&quot;bC0k&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;ypS6&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;PU1h&quot;&gt;8.2. Работа с переменными окружения&lt;/h3&gt;
  &lt;p id=&quot;gydO&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;22LL&quot;&gt;- Используем утилиту &lt;strong&gt;getEnvVar&lt;/strong&gt; для гибкой работы с переменными окружения с учётом контура (dev, stage, prod).&lt;/p&gt;
  &lt;p id=&quot;LWF4&quot;&gt;&lt;br /&gt;- Все секреты и ключи — только в &lt;strong&gt;.env&lt;/strong&gt;, не коммитим в репозиторий.&lt;/p&gt;
  &lt;p id=&quot;Z8Ak&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;bAax&quot;&gt;&lt;strong&gt;Пример&lt;/strong&gt;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;export function getEnvVar(configService: ConfigService, key: string, fallback?: string) {&lt;br /&gt;  let env = (configService.get&amp;lt;string&amp;gt;(&amp;#x27;NODE_ENV&amp;#x27;) || process.env.NODE_ENV || &amp;#x27;development&amp;#x27;).toLowerCase();&lt;br /&gt;  if (env.startsWith(&amp;#x27;dev&amp;#x27;)) env = &amp;#x27;dev&amp;#x27;;&lt;br /&gt;  else if (env.startsWith(&amp;#x27;prod&amp;#x27;)) env = &amp;#x27;prod&amp;#x27;;&lt;br /&gt;  else if (env.startsWith(&amp;#x27;stag&amp;#x27;)) env = &amp;#x27;stage&amp;#x27;;&lt;br /&gt;  env = env.toUpperCase();&lt;br /&gt;  return (&lt;br /&gt;    configService.get&amp;lt;string&amp;gt;(&amp;#x60;${env}_${key}&amp;#x60;) ||&lt;br /&gt;    configService.get&amp;lt;string&amp;gt;(key) ||&lt;br /&gt;    fallback&lt;br /&gt;  );&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Это позволяет легко деплоить один и тот же код в разные окружения без правок.&lt;/p&gt;
  &lt;p id=&quot;U7pU&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;arpQ&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;2Yxb&quot;&gt; Health-check и базовые контроллеры&lt;/h3&gt;
  &lt;p id=&quot;nnmS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;4WvP&quot;&gt;- В проекте реализованы базовые контроллеры (например, app.controller.ts) для проверки работоспособности сервиса.&lt;/p&gt;
  &lt;p id=&quot;0fh6&quot;&gt;&lt;br /&gt;- DTO даже для простых ответов (например, HelloResponse) — это удобно для автотестов, мониторинга и Swagger.&lt;/p&gt;
  &lt;p id=&quot;H6U2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;lbWy&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;nNco&quot;&gt;9. Пошаговый чек-лист для внедрения best practices&lt;/h2&gt;
  &lt;p id=&quot;TNBI&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;RoBX&quot;&gt;&lt;strong&gt;1. Везде используем DTO, entity наружу не возвращаем.**&lt;br /&gt;2. Пагинация и идентификаторы — только через shared/dto.**&lt;br /&gt;3. Логирование — через Logger, логируем всё важное.**&lt;br /&gt;4. Валидация — через class-validator, ошибки — через фильтр.**&lt;br /&gt;5. Swagger — для всех DTO и эндпоинтов.**&lt;br /&gt;6. Безопасность — не возвращаем пароли, используем guards.**&lt;br /&gt;7. Тесты — для всего, что может сломаться.**&lt;br /&gt;8. Docker — для всех окружений, переменные — через .env.**&lt;br /&gt;9. Рефакторинг — всё общее в shared, всё техническое в common.**&lt;br /&gt;10. Проверяем, что проект собирается, тесты проходят, Swagger актуален.**&lt;br /&gt;11. Health-check эндпоинты — для мониторинга и CI/CD.**&lt;br /&gt;12. Паттерн “конструктор с partial” — для entity.**&lt;br /&gt;13. Гибкая работа с конфигами через ConfigService и утилиты.**&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;MPnh&quot;&gt;---&lt;/p&gt;
  &lt;h2 id=&quot;kIkj&quot;&gt;&lt;/h2&gt;
  &lt;h3 id=&quot;Jfkw&quot;&gt;Бонус: советы из реального опыта&lt;/h3&gt;
  &lt;p id=&quot;3GTl&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;rj2r&quot;&gt;&lt;strong&gt;- Не бойтесь удалять и переписывать код.  &lt;br /&gt;&lt;/strong&gt;  Лучший код — тот, который легко удалить или заменить.&lt;/p&gt;
  &lt;p id=&quot;Zrtz&quot;&gt;&lt;br /&gt;&lt;strong&gt;- Документируйте не только API, но и архитектурные решения. &lt;br /&gt;&lt;/strong&gt;  Почему выбрали именно такой подход? Это поможет новичкам и будущим вам.&lt;/p&gt;
  &lt;p id=&quot;Guzd&quot;&gt;&lt;br /&gt;&lt;strong&gt;- Внедряйте CI/CD с самого начала.&lt;br /&gt;&lt;/strong&gt;  Даже простая проверка тестов и линтера в pull request спасёт от многих багов.&lt;/p&gt;
  &lt;p id=&quot;e2qG&quot;&gt;&lt;br /&gt;&lt;strong&gt;- Общайтесь с командой.&lt;br /&gt;&lt;/strong&gt;  Лучшие архитектурные решения рождаются в диалоге.&lt;/p&gt;
  &lt;p id=&quot;iDF4&quot;&gt;&lt;br /&gt;&lt;strong&gt;- Не забывайте про мониторинг и алерты. &lt;br /&gt;&lt;/strong&gt;  Логирование — это хорошо, но оповещения о сбоях — ещё лучше.&lt;/p&gt;
  &lt;p id=&quot;SSh3&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;ScdS&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;EMNO&quot;&gt;Финал&lt;/h2&gt;
  &lt;p id=&quot;eVMM&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;XeEy&quot;&gt;Мы построили backend, который:&lt;br /&gt;- легко поддерживать и расширять,&lt;br /&gt;- безопасен и прозрачен для команды,&lt;br /&gt;- удобен для фронта и мобильных приложений,&lt;br /&gt;- легко масштабируется и деплоится в любое окружение.&lt;/p&gt;
  &lt;blockquote id=&quot;NxT3&quot;&gt;Этот опыт — результат десятков итераций, исправления ошибок, внедрения best practices и постоянного рефакторинга.  Следуйте этим принципам — и ваш проект будет радовать и команду, и бизнес!&lt;/blockquote&gt;
  &lt;p id=&quot;LgNZ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;fo3L&quot;&gt;Добро пожаловать в будущее ! &lt;br /&gt;&lt;/p&gt;

</content></entry></feed>