Перейти к содержанию

Архитектура SKUARO

Статус: ПРИНЯТА КАК ЦЕЛЕВАЯ АРХИТЕКТУРА

Обновлено: 2026-08-25.

Этот документ описывает целевую архитектуру продукта. Фактически реализованные части и следующий шаг всегда уточняются в docs/STATUS.md.

Архитектурные принципы

  • SKUARO имеет открытую функциональную границу и может включать CRM, коммуникационные, AI и другие необходимые возможности; архитектурные границы определяются модулями, contracts, данными и permissions.
  • Основное приложение строится как модульный монолит с одним API, одной основной PostgreSQL и отдельным worker-процессом.
  • PostgreSQL является источником истины для бизнес-данных, сессий, аудита, outbox и очередей первого этапа.
  • Критические пользовательские сценарии работают без AI и внешних интеграций.
  • Контракты между frontend и backend формируются из общей схемы, а не поддерживаются вручную в двух местах.
  • DEV, STAGE и PROD изолируют данные, секреты, сессии, сети и deploy-конфигурацию.
  • Один собранный артефакт продвигается из STAGE в PROD без пересборки.

Общая схема

Browser
  -> HTTPS reverse proxy
     -> React/Vite static application
     -> Fastify /api/v1
        -> application modules
        -> PostgreSQL
        -> transactional outbox

Worker
  -> pg-boss queues in PostgreSQL
  -> email, notifications and AI adapters only when enabled

Целевой стек

Область Решение
Runtime Node.js 24 LTS, TypeScript
Frontend React 19, Vite, React Router, TanStack Query, React Hook Form
UI существующий packages/ui, Radix/shadcn-подход, Storybook
API Fastify, REST /api/v1, TypeBox, JSON Schema, OpenAPI
База данных PostgreSQL 18
Доступ к данным Drizzle и сохранённые SQL-миграции
Фоновые задачи отдельный worker и pg-boss
Тесты Vitest, интеграционные тесты с PostgreSQL, Playwright
Развёртывание Docker Compose, reverse proxy, health checks
Документация Material for MkDocs 9.x OSS в отдельном Docs-репозитории

Runtime поддерживает Node.js >=24 и npm >=11 без точной фиксации patch-релизов. Ключевые версии зависимостей локальной основы фазы 1B: React 19.2.8, Fastify 5.12.1, TypeBox 1.3.16, Drizzle 0.45.2, pg-boss 12.27.0 и PostgreSQL image 18.4-bookworm с зафиксированным multi-platform digest. Полный воспроизводимый состав хранится в package-lock.json и Compose. better-auth@1.7.1 установлен для Auth PoC по решению 0013, но становится постоянной зависимостью только после сквозной PostgreSQL/email integration.

Структура product-монорепозитория

apps/
  web/
  api/
  worker/
modules/
  today/
    module.yaml
    web/
platform/
  shell/
    web/
  home/
    web/
  auth/
    web/
    server/
  admin/
    web/
  account/
    web/
integrations/
  email/
packages/
  ui/
  contracts/
  database/
  config/
  testing/
infra/
  compose/
  deploy/

Решение 0014 физически применено 2026-08-22. modules/today содержит только фактический frontend, manifest, tests и техническое описание; необязательные contracts/domain/server/database появятся только вместе с соответствующей реализацией. platform/ содержит реализованные Auth/Admin/Account capabilities, синтетическую Organizations-витрину, а с 2026-08-24 — локальные platform/shell и platform/home; integrations/email содержит Gmail SMTP и локальный Resend HTTPS adapters, а apps/web и apps/api выполняют композицию через публичные entrypoints. apps/worker пока не регистрирует продуктовые очереди, а packages/database содержит auth-таблицы без бизнес-модели организаций и today; вертикальный MVP-сценарий ещё не завершён.

Правило размещения

Каждая новая разработка до появления кода классифицируется как product module, platform capability, external integration, shared technical package, runtime app или infrastructure. При неоднозначности владелец проекта утверждает границу и точки интеграции до создания каталога.

  • modules/<module-id> — вертикальная граница лицензируемой пользовательской функции.
  • platform/ — Auth, organizations, access, module catalog, subscriptions и audit, которые обслуживают несколько модулей.
  • integrations/ — Gmail, Resend, Telegram и другие внешние adapters; сама интеграция не становится продуктовым модулем без отдельного решения.
  • packages/ — только переиспользуемые технические блоки; module-specific UI и business rules сюда не переносятся.
  • apps/ — composition roots для web, API и worker.
  • infra/ — Compose, images, Caddy, backup и runtime units.

Необязательные папки и каталоги будущих модулей заранее не создаются. Пути и идентификаторы — английские; UI-названия и документация — русские.

Направление зависимостей backend

HTTP route
  -> application service
     -> domain rules
        -> repository interface
           -> Drizzle/PostgreSQL adapter

Начальные platform и domain capabilities:

  • identity;
  • organizations;
  • platform-admin;
  • clients;
  • requests;
  • next-actions;
  • audit;
  • outbox.

Они не считаются отдельными тарифными product modules без отдельного решения. Уведомления, интеграции, AI-анализ и биллинг добавляются только после подтверждённой потребности.

Доменная модель

User [status, optional platform role]
  -> Membership [organization role]
     -> Organization
        -> Client
           -> Request
              -> NextAction

Основные правила:

  • пользователь потенциально может состоять в нескольких организациях, но MVP показывает одну активную организацию;
  • каждый NextAction принадлежит конкретному Request;
  • активный Request имеет не более одного текущего незавершённого NextAction;
  • быстрое добавление атомарно создаёт Client, Request, NextAction, AuditEvent и OutboxMessage;
  • today, overdue и later вычисляются по dueAt и часовому поясу организации, а не хранятся отдельными статусами;
  • все бизнес-таблицы содержат organization_id;
  • обычный жизненный цикл использует архивирование вместо физического удаления;
  • даты событий хранятся как UTC timestamptz, часовой пояс организации обязателен;
  • идентификаторы — UUID, имена в БД — snake_case, в TypeScript — camelCase.

Минимальные технические сущности: AuthSession, AuditEvent, OutboxMessage.

Авторизация и доступ

  • Открытая самостоятельная регистрация разрешена.
  • Регистрация использует email и пароль; доступ к продукту закрыт до подтверждения email.
  • Подтверждение email открывает отдельный /onboarding/organization; организация, membership owner, entitlement today, audit и outbox создаются одной транзакцией только после отправки onboarding-формы по решению 0024.
  • Роль owner отображается как «Руководитель организации» и назначается только в организации, которую пользователь зарегистрировал.
  • Пароль содержит от 5 до 128 символов из печатного ASCII-диапазона U+0021–U+007E; пробелы, кириллица и emoji запрещены, обязательного состава по группам символов нет.
  • Вход выполняется по подтверждённому email и паролю.
  • Восстановление доступа выполняется через подтверждённый email.
  • Серверные сессии хранятся в PostgreSQL; браузер получает Secure, HttpOnly, host-only cookie.
  • DEV, STAGE и PROD используют разные cookies, сессии и секреты.
  • Продукт владеет моделями Organization и Membership; auth-библиотека не становится источником истины для организаций.
  • Права разделены на платформенный и организационный контуры. Глобальная роль super_admin не является membership организации; она имеет полный доступ внутри приложения и отдельный административный dashboard.
  • Глобальная роль platform_admin имеет те же продуктовые права, кроме любых изменений учётных записей super_admin и назначения или отзыва роли super_admin; ограничение обязательно проверяется backend.
  • Архитектурно предусмотрены роли организации owner, admin, member, viewer; подтверждены название и правило назначения owner, права остальных ролей определяются позднее.
  • User.status принимает active / inactive; неактивный пользователь не может войти или выполнять защищённые API-действия, а его активные сессии отзываются.
  • Email-verification хранит время и способ подтверждения. Обычный способ использует ссылку из письма; только super_admin может применить super_admin_manual с обязательными причиной и аудитом. Оба способа подтверждения открывают одинаковый organization onboarding и не создают tenant автоматически.
  • Административная смена пароля никогда не читает текущий пароль: она задаёт новый credential и отзывает прежние сессии.
  • Удаление пользователя или администратора в начальной модели означает деактивацию и архивирование, а не физическое удаление строки.
  • Backend проверяет эффективные права для каждого действия; видимость меню на frontend повторяет права, но не заменяет серверную авторизацию.
  • Backend проверяет membership для каждого доступа к данным организации и не доверяет organizationId от frontend.
  • Смена пароля отзывает активные сессии; значимые события доступа записываются в аудит.
  • Auth PoC использует Better Auth на /api/v1/auth/*, Drizzle adapter и PostgreSQL-таблицы auth_user, auth_account, auth_session, auth_verification; cookie cache отключён, чтобы отзыв серверной сессии применялся немедленно.
  • Resend выбран решением 0025 как основной кандидат DEV email transport через HTTPS 443; Gmail SMTP сохранён как fallback. До production отдельно выбираются провайдер, доменная аутентификация отправителя, bounce/complaint handling, suppression policy, лимиты и monitoring.
  • SSO, social OAuth, passkeys и impersonation не входят в первый MVP.
  • Будущий отдельный реестр неподтверждённых регистраций получает только email; password hash, verification tokens, session и organization data в него не копируются. Сам реестр и письма о продолжении регистрации пока не реализуются.

Главный экран и каркас приложения

Главный экран /home является общей platform capability platform/home, а не продуктовым модулем. Общий platform/shell владеет AppShell, навигацией и текущим пользовательским, организационным или платформенным контекстом. apps/web только собирает маршруты, а packages/ui предоставляет чистые переиспользуемые примитивы.

Стандартный вход ведёт на /home; разрешённая защищённая deep-link может быть восстановлена через next после проверки сессии и permissions. Корень / разрешает состояние доступа: guest → /login, неподтверждённый email → /check-email, подтверждённый пользователь без организации → /onboarding/organization, готовая учётная запись → /home. Базовая home.view не заменяет permissions виджетов, модулей и tenant-данных.

Главная формируется из эффективных capabilities:

  • организационные роли получают соответствующие доступу сводки, личные действия, команду и активные модули;
  • platform_admin и super_admin получают платформенную сводку и административные задачи;
  • совмещённая platform role и organization membership требуют явного переключения контекста;
  • tenant-данные не показываются в платформенном контексте без отдельно выбранной и проверенной организации.

Все защищённые внутренние страницы используют один AppShell. Визуальные области зафиксированы решением 0018 и UI_SYSTEM.md как S1–S3 для постоянной оболочки, P1–P6 для содержимого страницы и O1–O3 для временных слоёв. Auth использует отдельный AuthShell; публичный Site и Docs имеют собственные оболочки.

modules/today владеет только содержимым и действиями модуля /today. Главная может встраивать компактную сводку «Сегодня», но не переносит в platform/home доменную логику модуля.

Продуктовые модули и права

Продуктовый модуль является лицензируемой пользовательской функцией, а технический модуль — границей кода. Эти уровни связаны, но не обязаны совпадать один к одному.

Первый продуктовый модуль:

ProductModule
  id: today
  name: Сегодня
  audience: Руководитель организации
  purpose: dashboard состояния клиентской работы организации

Код этой продуктовой границы живёт в modules/today. Его module.yaml фиксирует постоянный ID, UI-название, lifecycle status, маршруты, permissions и явные зависимости. Lifecycle status разработки не подменяет активацию модуля в тарифе или конкретной организации.

Целевые связи:

Plan -> PlanModule -> ProductModule
Organization -> OrganizationModule -> ProductModule
Membership -> MemberModuleAccess -> ModulePermission

Эффективный доступ обычного пользователя вычисляется как пересечение:

active User
  + active Membership
  + enabled OrganizationModule
  + enabled MemberModuleAccess
  + allowed ModulePermission
  • Тариф задаёт максимально доступный организации набор модулей.
  • Руководитель организации может распределять доступ сотрудников только внутри подключённых модулей.
  • Внутри модуля backend проверяет конкретную операцию и сущность; frontend строит меню из уже вычисленных прав.
  • Связь продуктовых модулей реализуется через application services, contracts и события. Прямой неограниченный доступ одного модуля к внутренним таблицам другого не становится публичным контрактом.
  • Каждый модуль раскрывает только явные публичные entrypoints; циклические зависимости и импорт внутренних repository запрещены.
  • Модульные schema sources и contracts могут жить внутри module folder, но порядок SQL-миграций и общий OpenAPI собираются центральными runner/aggregator.
  • Cross-module E2E, migration и restore checks остаются в корневом tests/.
  • npm run boundaries:check автоматически проверяет обязательные поля module.yaml, публичные entrypoints, запрещённое направление импортов и циклы между границами.
  • Платформенные права super_admin и platform_admin вычисляются отдельно и сохраняют защиту учётных записей super_admin.
  • Названия и состав тарифов, биллинг и следующие продуктовые модули остаются отложенными решениями.

API и frontend

Начальный набор маршрутов:

/health/live
/health/ready
/api/v1/auth/*
/api/v1/session
/api/v1/organization
/api/v1/settings
/api/v1/admin/users*
/api/v1/admin/administrators*
/api/v1/today
/api/v1/clients*
/api/v1/requests*
/api/v1/requests/:id/next-action
/api/v1/intake

Frontend использует только /api/v1, отправляет cookies через credentials: include и не хранит токены в localStorage. Drizzle-схемы и внутренние domain-модули не импортируются во frontend.

Состояние frontend разделяется так:

  • server state — TanStack Query;
  • формы — React Hook Form;
  • локальное состояние интерфейса — React state;
  • общий глобальный store не добавляется до доказанной необходимости.

Целевая карта внутренних маршрутов: /, /home, /onboarding/organization, /today, /settings/profile, /settings/security, /organization/settings/general, /organization/settings/members, /organization/settings/modules, /organization/settings/permissions, /admin, /admin/users, /admin/administrators, /admin/organizations. /settings и /organization/settings остаются совместимыми redirect на начальные подразделы. /settings/notifications и /organization/settings/billing отложены до появления соответствующей функциональности. Auth-маршруты: /register, /login, /forgot-password, /reset-password, /check-email. Будущие продуктовые маршруты: /clients, /clients/:id, /requests/:id.

Административные маршруты доступны по эффективным правам super_admin и platform_admin; ручное подтверждение без email и защищённые операции над super_admin доступны только super_admin. Профиль доступен владельцу активной подтверждённой учётной записи; целевые permissions мутаций — account.profile.update и account.security.manage. Настройки организации проверяют organization.settings.view/organization.settings.manage и более узкие organization.members.manage, organization.modules.view, organization.modules.manage, organization.permissions.manage. Полная матрица admin/member/viewer остаётся отложенной.

На 2026-08-24 /home, общий AppShell, /settings/profile и /settings/security реализованы локально и приняты для handoff. Организационный onboarding, context switcher и /organization/settings/* ещё не реализованы: они ожидают фактические Organization, Membership и permissions. Развёрнутый DEV runtime не обновлялся и не подтверждает эту локальную реализацию.

Транзакции, очередь и AI

  • Бизнес-транзакция сохраняет данные, аудит и outbox атомарно.
  • Dispatcher переносит outbox-сообщения в очереди pg-boss.
  • Каждая задача содержит organizationId, correlationId и ключ идемпотентности.
  • Используются retry с backoff, dead-letter состояние, лимиты организации и аварийное отключение worker-функций.
  • Worker получает отдельного пользователя PostgreSQL с минимальными правами.
  • AI вызывается только через собственный небольшой AiProvider interface.
  • AI готовит черновик и не меняет бизнес-данные автономно.
  • Первый возможный AI-сценарий — ежедневная сводка владельца после появления достаточных данных.

Redis, vector database, RAG, LangChain, GigaChain, Hermes, Kafka, RabbitMQ, NATS, Kubernetes и микросервисы в начальную архитектуру не входят.

Среды и домены

Домен Назначение Контур
dev.skuaro.top DEV продукта общий тестовый сервер
dev-doc.skuaro.top документация разработчика отдельный контур тестового сервера
skuaro.top STAGE презентационного сайта отдельный контур тестового сервера
lk.skuaro.top STAGE продукта отдельный контур тестового сервера
doc.skuaro.top STAGE пользовательской документации отдельный контур тестового сервера
skuaro.com публичный лендинг production-контур
www.skuaro.com redirect на skuaro.com production-контур
lk.skuaro.com production продукта отдельный production-сервер
doc.skuaro.com документация пользователя production-контур

Same-origin API:

  • dev.skuaro.top/api/v1;
  • lk.skuaro.top/api/v1;
  • lk.skuaro.com/api/v1.

Product DEV и STAGE публично открывают Auth-маршруты, а текущие /settings, /today, /admin/* и целевые /home, /settings/*, /organization/settings/* вместе с защищёнными API проверяют сессию и backend-права SKUARO. По решению 0030 Basic Auth не применяется ни к Product, ни к Site, Docs или API reference без нового прямого запроса владельца. Все тестовые origins сохраняют noindex; приложение использует собственную SKUARO Auth там, где маршрут требует сессию или permissions.

STAGE использует production-like Product behavior: открытая самостоятельная регистрация, реальная email verification, sessions, roles и permissions без test-only code paths. Среда отличается от PROD только изоляцией runtime, DB, credentials, domains и данных.

Целевая схема решения 0019 ещё не развёрнута: фактически skuaro.top продолжает обслуживать прежний STAGE приложения, а новые .top-поддомены требуют отдельной настройки DNS, Caddy и deploy.

Runtime-изоляция

На тестовом сервере используются отдельные Compose projects:

  • skuaro-dev;
  • текущий skuaro-stage до миграции доменов;
  • целевые независимые STAGE-контуры Site, App и user docs после реализации решения 0019.

Developer docs разворачиваются как отдельный контур на dev-doc.skuaro.top без доступа к PostgreSQL продукта.

Ветка и сервер STAGE могут содержать исходники, тесты, миграции, checkout репозитория, .git, внутреннюю документацию, агентские правила и инструменты, если они нужны для воспроизводимой сборки, тестирования, диагностики или работы. Категорического требования оставлять на сервере только immutable runtime-артефакты нет. При этом секреты не попадают в Git, тестовые данные остаются синтетическими, а завершённость функционала и изоляция сред обязательны.

PROD позднее использует skuaro-prod на отдельном сервере. Каждая среда имеет отдельные network, PostgreSQL, volumes, credentials, runtime config, логи и backup policy. База данных не публикуется наружу.

Целевой reverse proxy — Caddy. Перед изменением сервера проводится read-only аудит; совместимый и безопасно работающий Nginx не заменяется автоматически.

Решение 0026 закрепляет единый security contract для роли Product App во всех средах: Fastify редактирует verification/reset token до сериализации request log, edge и app Caddy фильтруют URI и Referer в process/error logs, sensitive Auth callback/reset routes не попадают в edge access log, а App возвращает глобальный Referrer-Policy: no-referrer. Contract привязан к роли runtime, а не к домену: сейчас его используют dev.skuaro.top и временный legacy STAGE App на skuaro.top, при реализации решения 0019 он переносится на lk.skuaro.top, а будущий PROD App на lk.skuaro.com обязан сохранить эквивалентную защиту. Site и Docs не наследуют App policy автоматически.

Миграции и резервирование

  • SQL-миграции сохраняются в Git и применяются журналируемым одноразовым Compose service migrate до запуска API и worker.
  • db push запрещён в STAGE и PROD.
  • Изменения схемы выполняются последовательностью expand -> migrate -> switch -> cleanup.
  • Перед рискованным изменением DEV создаётся dump.
  • STAGE получает ночной logical dump и регулярную проверку восстановления.
  • PROD должен получать ежедневную зашифрованную off-server копию; WAL/incremental backup добавляется после уточнения нагрузки и RPO/RTO.
  • Откат приложения использует предыдущий неизменный image digest; откат несовместимой схемы не подменяется автоматическим downgrade.

Релизы

Формальная версия присваивается только продукту на lk.skuaro.com:

  • DEV: dev-<shortsha>;
  • STAGE candidate: YY.MM.DD.NN-rc;
  • PROD: YY.MM.DD.NN;
  • Git tag: app-vYY.MM.DD.NN.

Пример: [SERVER_ADDRESS]. Последнее число увеличивается для каждого production-релиза внутри одного дня.

Версия передаётся как release metadata или APP_VERSION, но не записывается четырьмя сегментами в package.json, потому что это не SemVer. API версионируется отдельно через /api/v1, миграции — timestamp-идентификаторами.

Лендинг и документация используют собственные build ID, например site-20260821-<sha> и docs-20260821-<sha>, и не меняют версию продукта.

Продвижение в STAGE

После реализации решения 0020 разрешённый merge DEV -> STAGE является точным событием готовности и автоматически запускает deploy соответствующего STAGE-контура. Дополнительное подтверждение deploy после merge не требуется. До явной активации pipeline в docs/STATUS.md ветка, GitHub Environment, deploy credentials, DNS и сервер продолжают изменяться только по отдельному разрешению.

Promotion pipeline разделяет наблюдаемые gates G1–G12: scope, static, unit, database, build, security, local E2E, deploy, stage smoke, role E2E, cross-domain и restore. Любой обязательный FAIL или BLOCKED блокирует готовность; процентный балл не используется. Полная стратегия находится в docs/STAGE_TESTING_STRATEGY.md.

Product, Site и Docs продвигаются независимо. Merge одного репозитория обновляет только его STAGE-контуры; комплексный релиз фиксирует commit/build ID каждого затронутого репозитория. STAGE promotion никогда автоматически не означает PROD promotion.

Репозитории и источники истины

Репозиторий Назначение Статус
webuzateam/SkuAro-DEV product-монорепозиторий существует, текущий корень
webuzateam/SkuAro-Site публичный лендинг согласован, ещё не создан в этой работе
webuzateam/SkuAro-Docs user docs и developer docs согласован, ещё не создан в этой работе

Product-репозиторий является источником истины для ADR, контрактов, миграций, инфраструктуры, security/runbook и release metadata. Docs-репозиторий содержит руководство пользователя и руководство разработчика. Site-репозиторий содержит только публичный лендинг и его публичные материалы. Рабочие правила документации описаны в docs/DOCUMENTATION_STRATEGY.md.

OpenAPI генерируется из контрактов product-репозитория и публикуется в developer docs как артефакт, а не переписывается вручную.

Система AI-агентов разработки

Обязательные правила агентов остаются в AGENTS.md, решения — в ADR, текущее состояние — в docs/STATUS.md. Повторяемый подтверждённый опыт является общим для всех ролей и хранится в docs/agent-memory/; локальная память AI-клиента не заменяет эти источники.

Project Skills хранятся в .agents/skills. Перед созданием сравниваются цель, входы, выходы, область, permissions и риски: совпадающий контракт обновляет существующий Skill. Базовые Skills agent-learning и task-verification управляют накоплением опыта и доказательной приёмкой задач. Полный жизненный цикл описан в docs/AGENT_MEMORY_AND_SKILLS.md.

Project-scoped профили orchestrator, architect, frontend, backend, tester, reviewer хранятся в .codex/agents/, а лимит потоков — в .codex/config.toml. Модель не закрепляется и наследуется от родителя. Read-heavy анализ может выполняться параллельно; write-heavy реализация по умолчанию последовательна. Полные границы определены в docs/AGENT_ROLES.md.

Межагентный процесс использует статусы IN_PROGRESS -> READY_FOR_VERIFICATION -> VERIFIED -> ACCEPTED_FOR_HANDOFF. Заявление исполнителя не является доказательством. Следующий агент получает задачу только после проверки артефактов, матрицы критериев и применимых тестов по docs/AGENT_WORKFLOW.md. Для критической работы автор не может быть единственным проверяющим.

VERIFIED подтверждает качество результата, но не расширяет разрешения на Git, deploy, публикацию, установку, удаление, секреты или внешние ресурсы.

Отложенные решения

Результат product gate объявлен владельцем как ПОДТВЕРЖДЕНО 2026-08-21; «Сегодня» принят первым production-модулем. Отложены:

  • результат PoC Better Auth и резервный вариант auth-реализации;
  • production email-провайдер; Resend выбран только как основной кандидат DEV transport, Gmail SMTP сохранён как fallback;
  • consent, retention, удаление, частота писем и anti-abuse для отдельного реестра email неподтверждённых регистраций;
  • AI-провайдер после отдельного сравнительного PoC;
  • первая внешняя интеграция после подтверждения платными пилотами;
  • точные RPO/RTO, retention и monitoring stack;
  • правовые и эксплуатационные требования до ввода реальных клиентских данных;
  • бюджет и сроки до расходов или обязательства по дате.