Правила ведення документації

Аудиторія: автор сторінки (людина або AI) у момент створення/оновлення. Це довідник правил формату — «який тег, яка шапка, яка обов’язкова секція». Мета: за 10 секунд згадати потрібне правило під час написання.

Як ця документація фізично існує — у README. Процес створення (з чого почати, чекліст) — у AUTHORING.


Принципи

  1. Карта території, не покрокова інструкція. Фіксуй важливе так, щоб швидко орієнтуватись і розуміти картину, а не читати з початку.
  2. UI — хребет, бек — опорні картки. Читач стартує з UI (що бачить у програмі) і переходить на бек по лінках (що це тригерить / звідки оновлюється).
  3. API — це Swagger, не markdown. Контракт API живе в auto-Swagger самого сервісу (через routing-controllers). У docs описуємо тільки бізнес-нюанси і ставимо deeplink на конкретний endpoint у Swagger UI.
  4. Схематично + нюанси. Не описуємо що робить кожна стрічка. Описуємо що є, в якому порядку, які числа/умови, які нюанси і чому вони такі.
  5. Рішення, не реалізація. Якщо рішення неочевидне — фіксуй чому так зробили, не як зроблено на рівні синтаксису.
  6. Один файл — одна одиниця. UI-секція і бек-сервіс живуть у різних файлах і пов’язуються лінками.
  7. Файл ↔ нода Canvas. Кожен файл у docs/ має бути представлений як нода на Canvas (локальному Canvas.canvas сервісу або глобальному architecture/Canvas.canvas). Якщо підтема надто мала щоб мати окрему ноду — зливаємо з батьківським файлом у секцію.
  8. Не посилайся на те, чого немає на Canvas. Wiki-лінки [[X]] у тілі файлів можна ставити тільки на файли які представлені нодою на Canvas. Якщо хочеться послатись на дрібну підтему — злий її у файл який є на Canvas, і посилайся через [[файл#секція|Секція]].

Межа глибини

Описуємо:

  • Числа: таймаути, інтервали, ліміти, пороги
  • Умови, фільтрації, послідовність кроків
  • Залежності: хто що тригерить, хто від кого залежить
  • Нюанси поведінки що неочевидні з коду
  • Чому саме таке рішення — якщо неочевидне

Не описуємо:

  • Синтаксис виклику методів
  • Деталі транспорту (fetch vs axios, Promise.race vs Promise.all) — якщо не впливає на поведінку
  • Інфраструктура без бізнес-логіки (IPC, ping між вікнами, tab-to-tab запити)
  • Імена полів і типи які очевидні з коду
  • Запит/відповідь endpoint’ів — це робота Swagger, не markdown

Правило великого пальця: якщо без цього факту читач може прийняти помилкове рішення — описуємо. Якщо це тільки як зроблено, без впливу на поведінку — пропускаємо. У сумнівах — описуємо коротко в секції «Нюанси».

Людська мова, не імена з коду

Текст сторінки описує процеси зрозумілою людською мовою, без ідентифікаторів з коду. Не «sendMessage() викликає checkLimits()», а «перед відправкою повідомлення перевіряються ліміти». Імена функцій, методів і змінних у тілі сторінки не вживаємо — вони застарівають при рефакторингу і нічого не кажуть читачу який не дивиться в код.

Де імена з коду доречні:

  • H1 і контекстна стрічка бек-сторінок (назва класу + шлях у src/)
  • назви колекцій/таблиць і полів на Entity-сторінках
  • ключі settings / env-змінні / назви RMQ-черг — те, що читач буде шукати дослівно

Структура папок сервісу

Розкладка <service>/docs/. Розкладка root stack-docs/ — у README.

docs/
├── Canvas.canvas         # локальний Canvas сервісу
├── index.md              # навігаційний індекс сервісу
├── glossary.md           # сервіс-специфічні терміни (опційно)
├── ui/                   # UI-екрани, секції, колонки (фронт-сервіси)
├── services/             # бек-сервіси загального призначення
├── entities/             # сутності даних
├── flows/                # послідовності кроків (логін, ініціалізація)
└── system/               # інфраструктура (БД, settings, оновлення)

Базовий набір — згори. Кожен сервіс бере тільки те що йому треба (ui/ лише для фронту, flows/ коли є складні процеси). Якщо в сервісі є природна підгрупа яка не вміщається в одну з цих папок (наприклад інтеграції із зовнішніми системами, фонові воркери, інтервали, фічі) — створюй для неї окрему top-level папку, не вкладену:

docs/
├── services/             # generic бек-сервіси
├── workers/              # фонові воркери з таймером
├── intervals/            # тимчасові тригери
├── senders/              # сендери (chat / mail)
├── integrations/         # інтеграції із зовнішніми системами
├── features/             # бізнес-фічі цього сервісу (Statistics, Sender…)
├── ui-triggered/         # бек-сервіси що тригеряться з UI
└── ...

Список не закритий — додавай свою категорію якщо вона є природною групою. Головне правило: папок верхнього рівня може бути багато, але вкладеність — ні.

Канон іменування у сайдбарі

РівеньСтильЯк виходить
Top-level stack-docs (Architecture, Features, Services…)Title Casetitle: у frontmatter відповідного index.md
Сервіс (Stack Golden, Stack AI…)Stack Xauto-inject у CI (для акронімів — override у inject-doc-meta)
Папки всередині сервісу (ui, services, entities…)lowercase, kebab-caseслаг папки; title: не ставити
Файли (всі без винятку)kebab-case lowercase (course-service.md, login.md)назва файлу

Один погляд на сайдбар має давати ясно: Title Case = top-level концепт, Stack X = сервіс, lowercase скрізь = вміст сервісу. Жодних PascalCase у назвах файлів — навіть для бек-класів. Зв’язок «doc-файл ↔ клас у коді» вказуй у H1 і контекстній стрічці самого файлу.

Папки

  • lowercase, kebab-case (ui, services, entities, workers, ui-triggered, integrations).
  • Не ставити title: в <folder>/index.md — інакше сайдбар покаже Title Case і це створить дисонанс з сусідніми lowercase-папками. Виняток — <service>/docs/index.md (див. нижче).
  • Без вкладеності. Усі папки — на одному рівні в docs/. Якщо потрібна підгрупа (наприклад workers, senders, integrations) — створюй її як окрему top-level папку, не як services/workers/.

Файли

  • Завжди kebab-case, lowercase — без винятків. UI-екран, flow, entity, бек-сервіс, контролер — всі однаково (course-service.md, mentor-service.md, memory-chunk.md, login.md, operator-card.md). Зміст всередині — українською (H1, текст, секції); англійський kebab — лише для імені файлу.
  • Причина: одне правило без винятків → сайдбар однорідний, wiki-лінки передбачувані, URL у Quartz чисті. Зв’язок з кодовим класом (CourseService у course.service.ts) залишається через H1 і контекстну стрічку у самому файлі (# CourseService + `src/modules/course/course.service.ts`).
  • Home.md не використовуємо — вхід через Canvas.canvas.

Index-файли

Кожна папка в сервісі повинна мати свій index.md — інакше Quartz покаже сторінку папки як голий auto-листинг файлів без контексту. У файлі — опис призначення папки і ключові файли з людськими описами; Quartz додасть auto-листинг знизу як комплемент.

Рівеньtitle: у frontmatterЯк виходить
<service>/docs/index.md (root сервісу)потрібен (auto-inject Stack <X> коли немає)сайдбар: Stack Golden / Stack AI. Для акронімів (aiStack AI) — пиши вручну
<folder>/index.md (підпапка всередині)не ставитисайдбар: слаг папки lowercase. H1 у файлі і вміст — як завгодно, важлива тільки відсутність title:

Cкелет для нової підпапки:

---
tags: [Meta]
---
 
# Folder Name
 
Одна-дві фрази: що в цій папці і навіщо.
 
- [[file-one]] — короткий опис цього файлу і коли до нього звертатись
- [[file-two]] — ...

Цей bullet-скелет — для малої папки (≤4-5 сторінок). Коли сторінок багато — переходимо на таблицю з мітками покриття (нижче).

Мітки покриття в індексах

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

МіткаСенс
заповнено — основний контент є, читається як готова сторінка
🟡частково — є суттєвий контент, але лишились прогалини / #TODO
скелет — тільки шапка + пара нотаток, основне не описано

Це мітка повноти опису, не плутати зі стан-тегами сторінки (#draft, #reviewed, #TODO, #fix). Сторінка може бути ✅ заповнена і водночас нести #TODO — коли борг у коді, а не в доці (приклад: воркери golden — описані повністю, а #TODO маркує відкрите питання в логіці).

Підпапка з багатьма сторінками (<folder>/index.md): таблиця з колонкою Стан, згрупована по логічних групах (кожна група — окрема H2-таблиця або секція-сепаратор). Зверху — підсумок X ✅ · Y 🟡 · Z ⬜ (з N).

**Покриття:** 4 ✅ · 2 🟡 · 25 ⬜ (з 31). Легенда: ✅ заповнено · 🟡 частково · ⬜ скелет.
 
## Група А
 
| Стан | Сторінка | UI | Що робить |
|---|---|---|---|
| ✅ | [[folder/page-a]] | … | … |
| ⬜ | [[folder/page-b]] | … | … |

Root-індекс сервісу (<service>/docs/index.md): зведена таблиця ## Покриття документації — рядок на розділ, колонки ✅ / 🟡 / ⬜ / Разом + підсумковий рядок Разом.

## Покриття документації
 
| Розділ | ✅ | 🟡 | ⬜ | Разом |
|---|--:|--:|--:|--:|
| [[ui-triggered/index\|ui-triggered]] | 4 | 2 | 25 | 31 |
| [[workers/index\|workers]] | 12 | — | — | 12 |
| **Разом** | **16** | **2** | **25** | **43** |

Розділ де одиниця виміру інша (наприклад entities — реєстр колонок готовий, а окремих сторінок 0/~50) у числову таблицю не пхаємо — виносимо приміткою під таблицею.

Підтримка вручну. Цифри оновлює автор при заповненні: підняв ⬜→✅ — поправ підсумок у підіндексі і зведену таблицю в root. Один рядок правки, зате миттєвий сигнал прогресу і backlog.


Шаблон сторінки

Обов’язковий початок

#Тег1 #Тег2
 
[[Назва-файлу]]
 
# Людська назва
 
`контекстна стрічка: шлях в коді або "(UI-елемент)"` — одна фраза опису
  • Теги — тип сторінки (див. нижче). Один рядок, без ком.
  • Backlink [[Назва-файлу]] — для зворотного пошуку в Obsidian.
  • H1 — як називаємо в розмові (для UI), або назва класу/сервісу (для бек).
  • Контекстна стрічка — для бек: шлях `src/...ts`. Для UI: (UI-елемент) або (UI-секція). Для роле/ентіті — пропускаємо.

Опційні секції (використовувати за потреби)

## Суть
Один абзац: що це за елемент і яку роль він грає в системі.
 
## Use cases
Один абзац: хто і за яким сценарієм сюди приходить. Бізнес-контекст, не механіка
кліків. Обов'язкова для UI-сторінок (див. «Правила для UI-сторінок» нижче).
 
## Числа / Інтервали / Таймаути
Таблиця або список з бізнес-значеннями.
 
## Логіка / Умови / Фільтри
Пронумерований список або таблиця.
 
## Нюанси
Неочевидні моменти з поясненням *чому*.
 
## Зв'язки
- Тригерить: [[...]]
- Оновлюється від: [[...]]
- Читає: [[...]]
- Стартує: [[...]]
 
## Чому так
Пояснення неочевидних архітектурних рішень. Тільки коли справді є причина.

Порядок секцій — приблизно такий як вище, але можна адаптувати. Обов’язкова тільки «шапка» (теги + backlink + H1 + контекстна стрічка + одна фраза).


Типи сторінок і теги

Теги типу сторінки

ТипТегВміст
UI-елемент#UIЕкран, секція, колонка, діалог, тулбар
Сервіс#ServiceФоновий процес, сендер, runner, контролер
Інтервал#IntervalДодатковий тег для сервісів з таймером
Сутність#EntityСтруктура даних: чат, лист, нотатка, таск
Процес#FlowПослідовність кроків: логін, логаут, ініціалізація
Система#SystemІнфраструктура без бізнес-логіки (БД, settings, tabs)
Роль#RoleРоль доступу (director, supervisor, operator …)
API#APIОгляд API-групи бек-сервісу + лінки на Swagger
Архітектура#ArchitectureКрос-сервісна картина — лежить тільки в root
Мета#MetaСамі конвенції, індекси, глосарії

Теги типу можна комбінувати (#Service #Interval). Один з цих тегів обов’язковий у шапці кожного файла.

Сервісні теги (автоматичні)

Кожен файл під services/<service>/ на сайті отримує тег з назвою сервісу (#electron, #golden, #client, #stack, #prime, #udate, #chathouse, #academy, #ai). Це робить inject-doc-meta при білді — руками не писати.

Для чого: на зібраному сайті клік по тегу #electron дає список усіх документів у відповідному сервісі.

Винятки коли варто додати сервісний тег руками: root-документ який крос-сервісний, але переважно про конкретний сервіс — наприклад entities/<X> де сутність живе в RMQ-обміні, але споживається переважно одним сервісом. Тоді в шапку додаємо #<service> руками — він з’явиться як додатковий ярлик.

Стан-теги

Документ за замовчуванням — чорновик (часто AI-генерація), сліпо не довіряй. Стан-теги додаються коли треба позначити особливий стан явно.

ТегСенсКоли ставити
#reviewedДокумент верифікований — людина пройшлася і перевірила що відповідає реальності.Ставимо після ручної перевірки. Якщо документ суттєво змінено — зняти, бо треба перевіряти заново. Клік по #reviewed на сайті дає список того чому довіряємо.
#draftДокумент написаний на гілці docs сервісу до того як відповідний код приземлився у master. Не зверений з реальним кодом.Ставимо коли пишемо док-перш. На сайті ці сторінки видно поряд з рештою — клік по #draft дає список того що чекає зверки.
#TODOДокумент незавершений або потребує уточнення.Коли лишаєш TODO: ... у тексті, додай тег у шапку. Клік по #TODO на сайті дає список усього що треба добити.
#fixОписана логіка є кривою/тимчасовою. Документуємо як є зараз, але хочемо переписати.Коли документ описує реальну поведінку коду який є костилем, hack’ом, технічним боргом. Не для UI-багів — для архітектурних плям. Сигнал «коли торкатимемось — переписати і код, і цей документ».

Стан-теги ставимо в тому ж рядку з тегом типу:

#Service #reviewed
#Service #draft
#UI #TODO
#Service #fix

Зніми #TODO коли всі недоробки в документі закриті. Зніми #fix коли код переписаний і документ оновлений. Зніми #reviewed якщо документ суттєво переписали — треба перевіряти заново.

Lifecycle #draft#reviewed. #draft живе на гілці docs сервісу — туди пишемо док-перш, поки відповідний код ще на feature-гілці. Коли код приземляється у master і доки мерджимо з docs у master — обов’язково проходимось по #draft-сторінкам, звіряємо опис з реальним кодом, і замінюємо #draft на #reviewed (або лишаємо без тегу якщо це чорновик не вартий статусу «довіряємо»). У master тегу #draft бути не повинно.

Теги — єдиний інструмент фільтрації, тримаємо їх дисципліновано.


Лінки і навігація

Wiki-лінки (всередині vault)

Всередині свого сервісу — звичайний [[Назва файлу]]:

Дивись [[Login screen]], тригерить [[OperatorRunner]].

На глобальний контент (root) — пишемо повний шлях від кореня vault:

Доступ обмежено [[roles|роллю operator]].
Користувач визначений у [[entities/Lady]].

На інший сервіс — повний шлях через services/:

Дивись [[services/golden/api/index|Golden API]].
Cleanup runner стартується по подіях [[services/stack/flows/UserSignup]].

Якщо ти редагуєш у режимі «один сервіс» (vault — тільки твій сервіс), крос-сервісні wikilinks показуватимуться сірими у твоєму локальному Obsidian. На зібраному сайті вони працюють. Це не баг — пиши лінк нормально.

URL-лінки (зовні)

  • Swagger endpoint — звичайний URL deeplink:
    [getOperatorLadies (golden)](https://api.besocial.tech/golden/api-docs/v2#/Electron%20API/ElectronApiController.getOperatorLadies)
    
  • Жодних інших зовнішніх систем у markdown без необхідності.

Правила лінкування

  • UI → бек: обов’язкова секція Зв'язки з лінками на сервіси/ендпоінти що цей UI використовує.
  • UI → API: не описуємо запит/відповідь у markdown — ставимо deeplink на Swagger. Якщо є бізнес-нюанс який Swagger не показує (особлива комбінація ролі + whitelist, наприклад) — описуємо саме нюанс.
  • Бек → UI: в секції Зв'язки — куди йде результат сервісу.
  • Бек → ентіті: лінк на [[entities/X]] (локальний або глобальний).
  • Не дублюй reverse-лінк на index/menu. Якщо сторінку X уже лінкує index/menu/матриця — не пиши в X.Зв'язки wikilink на цю index/menu. Quartz Backlinks-панель сама покаже всі вхідні посилання, а інакше Backlinks роздуваються 20-30 одноманітними entries без сенсу. Wikilinks у Зв'язки — лише для семантичного зв’язку (X пояснює Y, X читає Y, X залежить від Y). Це правило ще буде уточнюватися — зараз draft.

Правила для UI-сторінок

  • Назва — як звемо в команді («колонка фаворитів»), а не як в коді (FavoritesColumn.tsx)
  • Обов’язкова секція ## Use cases — один абзац: хто (роль/функція) і за яким сценарієм сюди заходить. Бізнес-контекст, не повторення > навігації і не матриця прав з Доступи. Якщо use case один — речення; якщо ролей з різними сценаріями кілька — bullet’и за ролями.
  • Обов’язкова секція Зв'язки — звідки дані (лінк на бек-ендпоінт у Swagger), що тригерить (лінк на дію)
  • Описуємо: які дані показуються, які дії доступні, які фільтри й стани, які обмеження
  • Не описуємо: верстку, стилі, компоненти, імена пропсів, форму запиту/відповіді

Канон структури: фічі + ендпоінти

Еталони для копіювання: Courses (single-backend) і Favorites (per-Family).

1. Таблиця кліків-фіч. Карта екрану одним поглядом: рядок = одна дія / під-екран. Коли доступ залежить від ролі чи призначення — колонки на access-тип (як Director / Mentor / Trainee у Courses). Деталі кожної фічі — нижче окремими секціями.

2. Ендпоінти — deeplink’и на Swagger, не контракт. Під фічами — посилання на конкретні Swagger-ендпоінти + 2-3 слова «що робить». Форму запиту/відповіді не пишемо (це робота Swagger). Формат залежить від типу екрану:

  • Single-backend (ui-shared/* — academy, stack…): під кожною фічею своя таблиця Endpoint | Що робить, одна колонка. Як у Courses.
  • Per-Family (ui-family/*): один екран працює на 4 Family (Udate / TalkyTimes / GoldenBride / Chathouse) з різними беками. Тому ендпоінти — таблиця з колонкою на Family: рядок = під-фіча, колонки golden / prime / udate / chathouse. Де бек ще не мігрований у Swagger — *TODO*. Як у Favorites (секція ## API).

Розбіжності полів між Family (на per-Family сторінці) фіксуємо окремою таблицею «поле × Family» з / (як у Favorites → «Таблиця фаворитів — поля по Family» + «Розбіжності»). Один UI-екран = 4 контракти, тому єдиного лінку «на бек» не буває — завжди розклад по Family.

Правила для Service-сторінок

  • Шлях у коді — обов’язковий у шапці
  • #Interval — якщо є таймер
  • Секції: Інтервал (якщо є), Логіка, Нюанси, Зв'язки
  • Нюанси зазвичай найцінніше: stop-листи, watchdog’і, захисти від подвійного запуску, рандомізації
  • Доступ за ролями — якщо ендпоінт має guard([...]), перерахуй ролі або пошлися на [[roles/<role>]]. Не дублюй логіку гварда — Swagger її показує. Описуй бізнес-причину обмеження якщо вона неочевидна.

Правила для Entity-сторінок

  • Описуємо структуру даних і ключові властивості сутності
  • Глобальні ентіті (живуть у RMQ-обміні між сервісами або в спільних таблицях) — у stack-docs/entities/
  • Локальні (тільки всередині одного сервісу) — в <service>/docs/entities/
  • Лінки на сервіси які цю сутність продукують/споживають

BE-схеми (Mongoose / SQL)

Коли сутність відповідає колекції/таблиці БД конкретного сервісу:

  • Контекстна стрічка`<шлях до схеми>` · колекція `<ім'я колекції>` (для SQL — таблиця). Приклад: `src/modules/course/schemas/course.schemas.ts` · колекція `academy_courses`.
  • Обов’язкова секція ## Поля — bullet-list з описом кожного поля що має бізнес-значення:
    - **name** — назва курсу
    - **role: `StackRoles`** — на яку роль курс розрахований
    - **mentors[]: `{mentorId, allowEdit}`** — Mentors курсу
    - **clientManagerId** — ObjectId на [[entities/<Other>]]
    Типи вказуй тільки коли неочевидні (enum, кастомний DTO, ObjectId-ref). Скаляри без типу. Тривіальні required: true / default: ... не описуй.
  • Секція ## Нюанси — індекси, каскадні видалення, кешовані поля, transform-плагіни, нетривіальні валідатори. Не вмикай у цей розділ загальний toJSON плагін якщо він однаковий по всьому сервісу — це описано в CLAUDE.md сервісу.
  • Не описуй Swagger-схему request/response DTO — для них є Swagger UI. Entity-сторінка про storage shape, не про API contract.

Реєстр сутностей

Кожен сервіс з ≥3 BE-сутностями веде <service>/docs/entities/index.md з тегом #Meta — список усіх колекцій/таблиць сервісу (включно з логами, буферами, технічним сміттям, для яких окремої entity-сторінки нема і не буде).

Формат: bullet-list з жирним підписом групи (не H3, бо H3 займає забагато вертикалі в рендері). Рядок:

**Admin / Agency**
- `golden_admins` — Адмін-акаунт партнерського API → —
- `golden_agensy_list` — Список зовнішніх агенцій → —
 
**Lady (TU)**
- `golden_lady` — TU, центральна сутність → [[Lady]]
  • Один bullet = одна колекція.
  • Назва колекції в ` (моноширинно), далі опис, в кінці → [[Name]] якщо є окрема entity-сторінка або → — якщо нема.
  • Якщо багато колекцій — групуй жирним підписом групи (**Group name**). Якщо багато сховищ (MongoDB + ClickHouse) — H2 на сховище, далі групи bold-ом.
  • Без полів — це індекс. Деталі лежать на самій entity-сторінці.

Правила для Flow-сторінок

  • Структура — пронумерований список кроків
  • Короткі flow (≤3 кроки) — описуємо в одному файлі
  • Довгі flow — окрема сторінка на крок, у головній тільки список з лінками
  • Крос-сервісні flow (наприклад user signup що зачіпає stack + golden + RMQ) — у stack-docs/architecture/flows/

API сервісу

У сервісних доках власних сторінок про API не пишемо — у <service>/docs/index.md ставимо лінк на корінь Swagger відповідного бека. Контракт описує сам Swagger.

Глобальна карта всіх Swagger-ів + єдина конвенція відповідей + крос-сервісні нюанси гвардів — у API Map.

Бізнес-нюанс конкретного ендпоінту якого Swagger не показує (рідкісна комбінація ролей, особлива побічна дія, legacy-quirk) — описуй у файлі відповідного сервісу або контролера, не створюй окрему api/ папку.

Правила для Role-сторінок

Усі ролі — в одному файлі roles на рівні root. Один рядок таблиці на роль (код + кому + коротке призначення). Не розводимо окремий файл на кожну роль — це довідник, а не онбординг.

Якщо якась роль вимагає глибшого опису (складна матриця доступів, багатокроковий процес видачі) — виносимо тільки її в окремий файл roles/<role>.md і лінкуємо з таблиці.


Конвенції бекенд-коду (DTO)

Глобальні правила для backend-сервісів які генерують Swagger з декораторів (academy NestJS, golden routing-controllers). Окремі сервіси можуть мати додаткові локальні нюанси у своєму CLAUDE.md, але базові правила DTO — тут.

Іменування

  • Request DTO — action-named: <Entity><Action>Dto. Приклади: AdminCreateDto, AdminEditDto, LadyChangePasswordDto.
  • Response DTO<Entity>ResponseDto. Приклади: AdminResponseDto, LadyResponseDto. Один клас на унікальну форму відповіді.
  • PascalCase скрізь.
  • Mongoose IXxx interface — алиас на response DTO: export type IAdmin = AdminResponseDto;. Якщо storage має додаткові поля що не віддаються назовні — interface IAdmin extends AdminResponseDto { _internal: string }.

Розташування файлів

DTO живуть у <module>/dto/ всередині свого модуля.

Правило файл/клас:

  • Top-level Response/Request DTO → свій файл <Name>ResponseDto.ts / <Name>RequestDto.ts. Клас і ім’я файлу збігаються.
  • Nested DTO використаний лише в одному батьку → у тому ж файлі що батько, без export. Це «внутрішня деталь» response shape, не самостійна сутність.
  • Якщо такий nested-клас починає юзатися ще десь → виносимо у власний файл <Name>Dto.ts і експортуємо.

Приклад:

agency/dto/
  AgencyListResponseDto.ts        ← { totalIds: number[] }
  AgencyCalendarResponseDto.ts    ← містить AgencyCalendarResponseDto + nested AgencyCalendarItem (без export)

AgencyCalendarItem лише всередині AgencyCalendarResponseDto, тому не виноситься.

Поля

  • Декоруємо class-validator декораторами (@IsString, @IsInt, @IsBoolean, @IsOptional, @ValidateNested, @Type). Swagger schema автогенерується.
  • Поле id додаємо у XxxResponseDto тільки якщо воно реально віддається назовні і споживач його використовує. Не додавати «про всяк випадок» якщо дублюється з _id мангузника який ніхто не читає.

Для запитів які не мають body / query DTO (порожній POST, GET без params) — DTO не пишемо.

Nested array (масив іншого DTO)

class-validator-jsonschema не вміє автовиведення items-типу для Cls[] навіть з @Type(() => Cls) — генерує generic Array ref який Swagger UI не резолвить. Для нашого стеку є helper з @it-monkeys/stack-commons:

import { NestedArray } from '@it-monkeys/stack-commons';
 
export class CalendarItem {
  @IsString() date: string;
  @IsInt() count: number;
}
 
export class ResponseDto {
  @NestedArray(() => CalendarItem)
  calendar: CalendarItem[];
 
  @IsInt()
  totalIds: number;
}

@NestedArray(() => Cls) всередині комбінує @ValidateNested({each:true}) + @Type(() => Cls) + @JSONSchema({type:'array', items:{$ref:'#/components/schemas/Cls'}}) в один декоратор.

Виняток до правила «nested = не експортуємо»: клас-айтем мусить бути export (Swagger schemas registry резолвить refs по імені класу, без експорту ім’я з namespace не виходить). Файлова структура та сама — nested класи живуть у файлі parent-DTO.

Для AI

  • Не плутати action-named DTO з generic: AdminCreateDto ≠ «якийсь admin DTO» — це request для /create.
  • Не змішувати Request і Response в одному файлі.
  • Не створювати nested DTO як окремий файл «про всяк» — спочатку інлайн у батька.

Canvas

Canvas.canvas — головний вхід у документацію.

  • Глобальний architecture/Canvas.canvas — мапа сервісів верхнього рівня. Ноди = сервіси. Стрілки = HTTP/RMQ-зв’язки.
  • Локальний <service>/docs/Canvas.canvas — мапа всередині сервісу (як зараз в електроні). Ноди = файли в docs/ цього сервісу.

Кольори по типах

ТипКолірHex
UIблакитний#2196f3
Serviceзелений#66bb6a
Entityфіолетовий#ab47bc
Flowпомаранчевий#ffa726
Systemсіро-зелений#78909c
Roleжовтий#ffd54f
APIбірюзовий#26a69a
Metaчервоний#e03131

Типи стрілок (підпис на стрілці)

  • тригерить — натискання/подія запускає процес
  • оновлює — сервіс пише дані в UI/entity
  • читає — UI читає дані з entity/store
  • стартує — один сервіс стартує інший (ініціалізація)
  • викликає — крос-сервіс HTTP виклик
  • публікує / слухає — RMQ події

Групи

Обводимо ноди одного типу в групу з кольоровим підписом (наприклад «Інтервали оператора», «Сервіси TU»). Використовуємо для ≥3 нод одного типу в одному місці.