diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 16de2f7..13c11c5 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -26,18 +26,6 @@ jobs: npm ci npm run docs - - name: Коммит generated/ - run: | - git config user.name "CI Bot" - git config user.email "ci@gromlab.ru" - git add generated/ - if git diff --cached --quiet; then - echo "ARCHITECTURE.md не изменился, пропуск" - else - git commit -m "docs: обновить ARCHITECTURE.md [skip ci]" - git push origin main - fi - - name: Автоматический тег (semver patch) id: tag run: | @@ -55,6 +43,20 @@ jobs: echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT echo "Создан тег: $NEW_TAG" + - name: Подставить URL с тегом и коммит + run: | + NEW_TAG=${{ steps.tag.outputs.new_tag }} + sed -i "s|raw/branch/main/generated|raw/tag/${NEW_TAG}/generated|" README_RU.md + git config user.name "CI Bot" + git config user.email "ci@gromlab.ru" + git add generated/ README_RU.md + if git diff --cached --quiet; then + echo "Нет изменений, пропуск" + else + git commit -m "docs: обновить ARCHITECTURE.md и README (${NEW_TAG}) [skip ci]" + git push origin main + fi + docker: runs-on: ubuntu-latest needs: docs diff --git a/.gitignore b/.gitignore index fb27d5e..c42ad84 100644 --- a/.gitignore +++ b/.gitignore @@ -140,5 +140,3 @@ docs/.vitepress notes -# Генерируемые файлы -README_RU.md \ No newline at end of file diff --git a/README_RU.md b/README_RU.md new file mode 100644 index 0000000..1e3b55c --- /dev/null +++ b/README_RU.md @@ -0,0 +1,98 @@ +# SLM Design +Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили. + + +Для AI-ассистентов доступен [единый файл правил](https://gromlab.ru/gromov/slm-design/raw/branch/main/generated/ru/ARCHITECTURE.md). + + +## Преимущества + +### Вертикальная организация домена + +Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы. + +### Разделение ответственности без перегрузки слоёв + +Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода. + +### Горизонтальная инкапсуляция + +Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга. + +### Колокация по умолчанию + +Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями. + +### Явное разделение каркаса и контента + +Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью. + +### Масштабирование через группировку + +При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции). + +### Dependency Injection без фреймворков + +Cross-domain зависимости в бизнес-слое реализуются через фабрики — модуль декларирует что ему нужно, а точка использования предоставляет зависимости. Домены изолированы без DI-контейнеров, провайдеров и шин событий. + +## Происхождение + +SLM Design вырос на основе: + +- **Feature-Sliced Design** — слоистая структура, публичный API модуля, направление зависимостей +- **Vertical Slice Architecture** — модуль как вертикальный срез, содержащий всё необходимое +- **Screaming Architecture** — структура проекта «кричит» о назначении: открыл `business/auth` — видишь авторизацию +- **Colocation Principle** — код живёт рядом с местом использования + +## Пример структуры проекта + +```text +src/ +├── app/ +│ +├── layouts/ +│ ├── main/ +│ └── dashboard/ +│ +├── screens/ +│ ├── home/ +│ ├── products/ +│ ├── product-detail/ +│ └── about/ +│ +├── widgets/ +│ ├── page-heading/ +│ ├── hero-section/ +│ └── promo-banner/ +│ +├── business/ +│ ├── auth/ +│ ├── catalog/ +│ ├── orders/ +│ └── chat/ +│ +├── infrastructure/ +│ ├── theme/ +│ ├── i18n/ +│ ├── backend-api/ +│ └── logger/ +│ +├── ui/ +│ ├── button/ +│ ├── input/ +│ ├── modal/ +│ ├── toast/ +│ └── dropdown/ +│ +└── shared/ + ├── lib/ + ├── types/ + └── styles/ +``` + +## Принципы + +- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле. +- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости. +- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API. +- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда. diff --git a/concat-md.js b/concat-md.js index a9d03bd..962d06a 100644 --- a/concat-md.js +++ b/concat-md.js @@ -73,4 +73,20 @@ const buildRules = (lang) => { buildRules("ru"); buildRules("en"); +// Генерируем README из index.md +const buildReadme = (lang, outFile) => { + const indexPath = `./docs/${lang}/index.md`; + + if (!fs.existsSync(indexPath)) { + console.log(`Пропуск README (${lang}): ${indexPath} не найден`); + return; + } + + const content = stripFrontmatter(fs.readFileSync(indexPath, "utf8")); + fs.writeFileSync(outFile, content, "utf8"); + console.log(`${outFile} создан из ${indexPath}`); +}; + +buildReadme("ru", "./README_RU.md"); +