Files
slm-design/old-docs/canons/file-atlas.md

28 KiB
Raw Blame History

title, description
title description
Атлас файлов SLM Карта слоёв, типов modules, root files, segments, public API, tests и запрещённых файловых сочетаний

Атлас файлов SLM

Используй атлас после того, как определены роль изменения и владелец ответственности. Не выбирай архитектуру по желаемому имени файла.

Атлас исчерпывает стандартные архитектурные роли SLM, но не является закрытым списком framework-файлов. Команда может добавить локальный segment или suffix, если его ответственность не дублирует существующую, не нарушает направление зависимостей и закреплена в локальных инструкциях.

Единицы структуры

Единица Что означает Имеет public API Владеет runtime
Layer Верхнеуровневая область ответственности внутри SLM root Нет общего требования Зависит от layer
Group Навигационная папка для modules или других groups Нет, index.ts запрещён Нет
Module Самостоятельный владелец одной ответственности Да Может
Segment Папка внутри module по назначению файлов Нет отдельного внешнего API Только как часть владельца
Component Презентационная часть родительского module в ui/ Локальный index.ts допустим Нет архитектурного runtime
Root file Главный entry/contract конкретного module Экспортируется module index.ts Зависит от типа module

Сначала определи module, затем root file, затем необходимые segments. Не создавай все папки из атласа заранее.

Карта SLM root

src/
├── app/             # framework wiring
├── compositions/    # product tree, graph owners и business integrations
├── business/        # доменные контракты и сценарии
├── infra/           # технические runtime-сервисы
├── ui/              # универсальные UI modules
└── shared/          # детерминированный фундамент

В monorepo вместо src/ границей приложения обычно является apps/{app}/src/. Packages находятся выше SLM root и не являются дополнительными слоями.

Root files modules

Pattern Роль Где допустим
{name}.page.tsx Готовая page composition compositions
{name}.layout.tsx Product layout composition compositions
{name}.screen.tsx Уникальный screen leaf/branch compositions
{name}.widget.tsx Самостоятельный composition block compositions
{name}.route.tsx Route composition и route lifecycle compositions
{name}.entry.tsx Готовая точка подключения product tree compositions
{scope}-business-composition.ts Non-visual сборка business graph/scope compositions
{name}.ts Другой non-visual root, названный по ответственности compositions, nested modules
{name}.tsx Root UI/module component без специальной роли compositions, ui, nested modules
{domainName}.factory.ts Единственный runtime entry business-домена business/{domainPath}
create-{domainName}-business.ts Builder одной business-фабрики compositions/business/{domainPath}
{name}.client.ts Технический client infra/{service}
{name}.service.ts Технический root service, если service и есть module entry infra/{service}
index.ts Public API конечного module В module; запрещён у group

Root suffix не определяет owner автоматически. Например, profile.store.ts остаётся domain store или page UI store в зависимости от смысла state.

Layer App

app содержит только файлы, требуемые framework/runtime entry.

Возможные файлы:

Файл или pattern Назначение
main.tsx, bootstrap.tsx Запуск приложения
app.tsx Тонкое подключение application entry/providers
app-router.tsx, router.tsx Framework route registry
page.tsx, layout.tsx, route.ts, error.tsx, not-found.tsx Framework-convention files
middleware.ts Framework middleware boundary
framework metadata/config files Только framework contract

Правила:

  • framework file импортирует готовый entry/route/page composition через public API;
  • product tree собирается в compositions, не в app;
  • business graph, domain store, screen, widget и product provider в app запрещены;
  • у app нет SLM modules и общего index.ts;
  • framework-specific server/client правила определяет профильный framework skill.

Минимальный пример:

src/app/
├── app-router.tsx
├── app.tsx
└── main.tsx

src/compositions/entries/profile/
├── profile.entry.tsx
└── index.ts

Consumer composition module

Consumer composition собирает product UI и использует готовые {Domain}Api.

compositions/{group}/{name}/
├── {name}.{page|layout|screen|widget|route|entry}.tsx  # visual entry, если нужен
├── {name}-business-composition.ts                      # business graph, если module владеет им
├── {name}.ts                                           # другой non-visual entry, если нужен
├── ui/                                                 # presentation components
├── parts/                                              # nested modules
├── providers/                                          # provider владельца scope
├── guards/                                             # route/UI guards над готовым DomainApi
├── hooks/                                              # access/orchestration hooks
├── stores/                                             # только локальный UI-state
├── services/                                           # orchestration готовых API
├── mappers/                                            # domain result -> ViewModel
├── types/                                              # module-owned types
├── errors/                                             # только composition/UI errors, не domain errors
├── lib/                                                # локальные deterministic helpers
├── config/                                             # module configuration
├── styles/                                             # styles module
├── tests/                                              # scope/integration tests при необходимости
└── index.ts                                            # public API

Не все segments обязательны. Создавай только используемые.

Composition module не обязан иметь .tsx: request/application business graph использует {scope}-business-composition.ts, а другая non-visual orchestration может иметь root .ts, названный по ответственности, и public index.ts.

Guard принадлежит composition scope, использует готовый {Domain}Api и выбирает route/UI outcome. Guard не получает product data напрямую, не создаёт business graph и не импортирует source runtime.

Consumer composition не содержит:

  • product SDK/client/generated operation;
  • product storage adapter;
  • source/query adapter домена;
  • domain store implementation;
  • domain mapper/error;
  • business factory implementation.

Product data поступает только через {Domain}Api. stores/ хранит presentation-only state: sidebar, tab, local step, transient form UI. Product entities и domain state туда не копируются.

Page/route graph owner

Если composition владеет business graph и lifecycle, возможна структура:

compositions/routes/profile/
├── profile.route.tsx
├── profile-business-composition.ts
├── providers/
│   ├── profile-business.provider.tsx
│   └── profile-business.context.ts
├── hooks/
│   └── use-profile-business.hook.ts
├── types/
│   └── profile-business.type.ts
├── tests/
│   └── profile-business-lifecycle.test.tsx
└── index.ts

Правила graph owner:

  • graph type перечисляет только реально доступные API;
  • Partial<Business> с cast к полному graph запрещён;
  • graph создаётся в lifecycle владельца;
  • domain lifecycle operation запускается после commit и возвращает cleanup;
  • raw SDK/client/event bus не импортируется для досборки домена;
  • screen/widget не создаёт тот же graph повторно.

Integration module business

compositions/business/{domainPath} является единственным module, который знает одновременно business dependency contract и concrete runtime.

{domainPath} означает полный относительный путь конечного business module, а {domainName} — имя его последней папки. При groups integration path зеркалирует business path:

business/app/auth/
compositions/business/app/auth/

business/cms/content/
compositions/business/cms/content/
compositions/business/{domainPath}/
├── create-{domainName}-business.ts         # обязательный builder
├── create-{domainName}-business.test.ts    # обязательный assembly test
├── adapters/                               # обязательны при runtime dependencies
│   ├── {source}.adapter.ts
│   ├── {storage}.adapter.ts
│   ├── {query}-hook.adapter.ts
│   ├── {state-manager}-state.adapter.ts
│   ├── {event-source}-events.adapter.ts
│   └── {platform}-navigation.adapter.ts
├── types/                                  # builder/cross-domain/request input types
│   ├── create-{domainName}-business-deps.type.ts
│   └── create-{domainName}-business-request-input.type.ts
├── testing/                                # assembly fixtures, не public API
└── index.ts                                # builder и type-only integration inputs

Builder:

  1. Явно создаёт или получает runtime instances нужного lifecycle без I/O.
  2. Создаёт adapters поверх runtime instances.
  3. Передаёт adapters и cross-domain API фабрике.
  4. Возвращает готовый {Domain}Api.

Browser/application builder без cross-domain dependencies вызывается без аргументов. Request-scoped builder отдельно принимает requestScopeInput с request data; concrete client factory импортируется integration module.

Integration module не содержит:

  • domain mapper/normalizer;
  • domain error;
  • business scenario;
  • React provider/layout/screen/widget;
  • full application graph;
  • exported private adapter.

Business module

Каждый business/{domainPath} имеет полный factory contract.

business/{domainPath}/
├── {domainName}.factory.ts                 # обязательно
├── index.ts                                # обязательно
├── types/                                  # обязательно для contracts
│   ├── {domainName}-api.type.ts
│   ├── {domainName}-deps.type.ts
│   ├── {domainName}-factory.type.ts
│   ├── {domainName}-error.type.ts
│   ├── {domainName}-error-code.type.ts
│   ├── {entity}.type.ts
│   ├── {source}-hook-result.type.ts
│   └── {domainName}-state.type.ts
├── errors/
│   └── {domainName}-business.error.ts
├── services/
│   └── {scenario}.service.ts
├── hooks/
│   └── use-{scenario}.hook.ts              # wrapper над dependency hook
├── mappers/
│   └── map-{entity}.ts                     # unknown -> domain
├── lib/
│   └── {domain-helper}.ts
├── config/
│   └── {domainName}.config.ts              # deterministic domain constants
└── tests/
    └── {domainName}-factory/
        ├── public-api.test.ts
        ├── {scenario}.test.ts
        └── testing/
            └── create-{domainName}-deps.mock.ts

Обязательный минимум:

  • {domainName}.factory.ts;
  • {Domain}Api, {Domain}Deps, {Domain}Factory;
  • domain error codes и собственная domain error для runtime failure;
  • index.ts с одним runtime export фабрики и type-only exports;
  • factory-level tests каждого public runtime operation;
  • integration builder и assembly test для каждого business-модуля, включая dependency-free factory.

Условные файлы:

  • services/ только при выделенном сценарии;
  • hooks/ только для wrapper над dependency hook;
  • mappers//normalizers//type guards при внешнем unknown;
  • errors/ при runtime operations;
  • colocated tests обязательны для каждого mapper, normalizer, type guard и domain error;
  • colocated tests services/hooks обязательны при самостоятельной branching, race или другой runtime-safe логике.

В business запрещены:

  • ui/, React components, providers, layouts и guards;
  • concrete stores/ Zustand/Redux/MobX;
  • adapters/ concrete runtime;
  • SDK/client/generated DTO;
  • SWR/TanStack Query/Apollo runtime или types;
  • React state/effect runtime или types;
  • storage/browser/event/env implementation;
  • raw external error в public API.

Infra module

Infra module владеет техническим сервисом без продуктовой модели.

infra/{service}/
├── {service}.client.ts                     # если module является client
├── {service}.service.ts                    # если module является service
├── client.ts                               # допустимый technical entry
├── config/
├── clients/
├── services/
├── transports/
├── hooks/                                  # technical hooks
├── providers/                              # provider technical service
├── ui/                                     # только technical UI, например theme tooling
├── errors/                                 # transport/technical errors
├── types/                                  # technical contracts/DTO
├── lib/
├── tests/
└── index.ts

Различай два вида adapter:

Вид Где живёт Что знает
Transport adapter/client infra Протокол, SDK, HTTP, transport types
Domain dependency adapter compositions/business/{domainPath} Business Deps и конкретный infra runtime

Infra не содержит business graph, domain state, product provider или domain error. Type-only imports business API не дают infra права агрегировать product graph.

UI module

UI module предоставляет универсальный UI без business logic и product I/O.

ui/{name}/
├── {name}.tsx                              # обязательный root component
├── ui/                                     # внутренние presentation components
├── parts/                                  # nested UI modules при самостоятельной роли
├── hooks/                                  # presentation behavior
├── stores/                                 # только локальный UI-state
├── providers/                              # UI scope provider
├── types/                                  # props и UI contracts
├── styles/
├── lib/                                    # presentation helpers
├── tests/
└── index.ts

UI module может строиться на других UI modules и shared. Он не импортирует business, infra или compositions, не получает product data самостоятельно и не выбирает источник.

Shared

shared содержит только детерминированный фундамент без знания о продукте и runtime-state.

shared/
├── lib/
│   └── {utility}/
│       ├── {utility}.ts
│       ├── {utility}.test.ts
│       └── index.ts
├── types/
├── styles/
├── config/                                 # только product-agnostic constants
├── assets/                                 # product-agnostic assets при локальном соглашении
└── sprites/                                # специализированная группа assets, если используется

Shared не содержит:

  • product/domain types;
  • stores и mutable singletons;
  • SDK/client wrappers;
  • React providers;
  • environment-dependent services;
  • imports из других SLM layers.

Components внутри ui segment

Presentation component родительского module имеет плоскую структуру:

{module}/ui/{component}/
├── {component}.tsx
├── types/
│   └── {component}-props.type.ts
├── styles/
│   └── {component}.module.css
└── index.ts

В папке component запрещены:

  • hooks/, stores/, services/, providers/, parts/;
  • source calls и scenario hooks;
  • imports project code вне parent module, кроме разрешённых UI modules;
  • nested components как отдельные architectural folders.

Если это требуется, сущность становится module и перемещается в parts/ либо на общий composition/UI уровень.

Nested modules в parts

Каждый элемент parts/ является полноценным module:

{parent}/parts/{part}/
├── {part}.tsx                              # visual root, если нужен
├── {part}.ts                               # non-visual root, если нужен
├── ui/
├── parts/
├── hooks/
├── stores/                                 # только state ответственности part
├── types/
├── styles/
├── tests/
└── index.ts

Одиночные .tsx, .ts или style files непосредственно в parts/ запрещены. Если nested module нужен за пределами parent, подними его в минимальный общий scope.

Segment matrix

Segment Consumer composition Business integration Business Infra UI Shared
ui/ Да Нет Нет Условно, technical UI Да Нет
parts/ Да Нет Нет Условно Да Нет
providers/ Да Нет Нет Да Условно Нет
guards/ Да, над готовым DomainApi Нет Нет Нет Нет Нет
hooks/ Да Нет Только wrappers над deps Technical hooks Presentation hooks Нет
stores/ Только UI-state Нет, используй adapters/ Нет concrete stores Technical state Только UI-state Нет
services/ Orchestration готовых API Нет, используй adapters/ Domain scenarios над deps Technical services Presentation-only Нет
adapters/ Нет product adapters Да Нет Только transport adapters по локальному соглашению Нет Нет
mappers/ Domain -> ViewModel Нет, transport adaptation остаётся в adapter unknown -> domain Transport mapping View mapping Чистые generic transforms
errors/ UI/composition errors Нет domain errors Domain errors Technical/transport errors UI errors Только generic errors
types/ Module contracts Integration input types Domain contracts Technical contracts/DTO Props/UI contracts Product-agnostic types
styles/ Да Нет Нет Условно Да Global/foundation styles
lib/ Local helpers Assembly helpers Domain deterministic helpers Technical helpers Presentation helpers Generic deterministic helpers
config/ Composition constants Runtime assembly config Domain constants Technical config/env UI constants Product-agnostic constants
tests/ Scope/integration tests Обязательные assembly tests Обязательные factory tests Technical tests UI tests Unit tests

Имена обычных файлов

Pattern Назначение
{name}.type.ts Module-owned type
{name}-props.type.ts Component props
{name}.hook.ts, use-{name}.hook.ts Hook владельца
{name}.store.ts Concrete store только допустимого owner/scope
{name}.service.ts Scenario или technical service по layer
{name}.adapter.ts Adapter с явно определённым видом
map-{name}.ts, normalize-{name}.ts Mapper/normalizer владельца
{name}.provider.tsx Provider владельца scope
{name}.guard.tsx Route/UI guard consumer composition
{name}.context.ts, {name}.context.tsx Private context implementation
{name}.error.ts Error соответствующего layer
{name}.config.ts Configuration владельца
{name}.constant.ts Константа владельца
{name}.module.css Styles конкретного module/component
{name}.test.ts, {name}.test.tsx Colocated test

Suffix описывает техническую форму, но не переносит ownership. user.type.ts не становится shared только потому, что это type; auth.store.ts не становится infra только потому, что использует Zustand.

Public API по типам modules

Module Runtime exports Type exports Не экспортировать
Consumer composition Entry/provider/access hooks Props, state/view types Raw context/store factory/internal parts
Business integration create{Domain}Business Cross-domain/request input types Adapters, clients, mocks
Business Только {domainName}Factory Api, Deps, Factory, domain/error types Services, hooks, mappers, error class
Infra Минимальный technical API Technical contracts Mutable internals и generated tree без необходимости
UI Root component и доказанные UI helpers Props/UI types Internal components/store/context
Nested module Root entry Props/module types Parent internals

Если дочерние layout/screen/widget импортируют access hooks владельца scope, не экспортируй из того же public API готовый entry, который импортирует эти дочерние modules. Раздели scope API и ready entry на отдельные composition modules, чтобы не создать runtime-цикл.

Tests map

Проверяемая граница Размещение Обязательность
Business public contract business/{domainPath}/tests/{domainName}-factory/ Обязательно
Business mapper/normalizer/type guard/domain error Рядом с файлом Обязательно для каждого такого файла
Business service/hook wrapper Рядом с файлом При самостоятельной branching/race/runtime-safe логике
Adapter и builder wiring compositions/business/{domainPath}/*.test.ts Обязательно для каждого business-модуля
Graph lifecycle/provider Tests graph owner module При state/subscriptions/resources
Consumer composition Рядом или tests/ module По поведению scope
Infra transport/service Внутри infra module По technical contract
UI module/component Внутри UI module По интерактивному поведению

Запрещённые структуры

business/auth/ui/                          # React UI внутри business
business/auth/stores/auth.store.ts         # concrete Zustand/Redux store
business/auth/adapters/backend.adapter.ts  # concrete runtime adapter
business/auth/types/sdk-response.type.ts   # generated/external DTO contract

compositions/pages/profile/services/api.ts # прямой product source
compositions/screens/profile/store.ts      # product/domain cache в screen

infra/business/                            # product graph/provider в infra
shared/user.type.ts                        # product domain type в shared

business/app/index.ts                      # group с public API
compositions/pages/index.ts                # group с public API

{module}/parts/hero.tsx                    # файл вместо nested module
{module}/ui/card/hooks/                    # component с собственной логикой

Новый тип файла или segment

Если нужной роли нет в атласе:

  1. Назови ответственность файла без технического suffix.
  2. Определи module-владельца и допустимые зависимости.
  3. Проверь, не является ли файл существующим service, adapter, mapper, provider или nested module.
  4. Создай новый segment только для нескольких файлов с одной устойчивой ролью.
  5. Не создавай global segment на уровне src/.
  6. Зафиксируй локальное соглашение, если pattern будет повторяться.
  7. Обнови template, если новый pattern стал обязательной повторяемой структурой.

Не подгоняй ответственность под красивое дерево. Минимальный корректный module лучше полного scaffold без реального поведения.