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

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

Подход

Для MVP выбран модульный монолит. Он дешевле микросервисов в разработке и эксплуатации, но сохраняет явные границы: каждый доменный модуль владеет use cases и таблицами, внешние сервисы скрыты за adapter-интерфейсами. Когда нагрузка или команда потребуют, worker расчётов/AI/уведомлений можно вынести без изменения публичного API.

Внутри бизнес-модуля сохраняется структура референса 21crush-backend:

router → controller → service/use case → repository → Prisma

  • Router задаёт URL, middleware и контракт HTTP.
  • Controller переводит HTTP в типизированную команду и формирует ответ.
  • Service реализует правила, транзакцию и права; Express/Prisma types туда не проходят.
  • Repository инкапсулирует запросы к данным.
  • Adapter инкапсулирует Swiss Ephemeris, YooKassa, AI provider, Resend и геокодер.

Потоки

flowchart LR
    C[Client API] --> R[Express routers]
    R --> S[Domain services]
    S --> P[(PostgreSQL)]
    S --> O[(Outbox/jobs)]
    W[Worker] --> O
    W --> E[Swiss Ephemeris]
    W --> Y[YooKassa]
    W --> A[AI provider]
    W --> N[Resend email]

Swiss Ephemeris — локальная библиотека и файлы данных, а не сетевой API. YooKassa и AI — внешние сетевые контуры.

Целевая структура

src/
  api/
    middlewares/
    routers/
  modules/
    config/
    logger/
    prisma/
  services/
    auth/
    users/
    places/
    calculations/
    astrology/
    numerology/
    human-design/
    destiny-matrix/
    interpretations/
    forecasts/
    billing/
    goals/
    practitioners/
    consultations/
    notifications/
    analytics/
    audit/
  shared/
    errors/
    types/
    utils/
  app.ts
  server.ts
tests/
generated/
docs/

Папки бизнес-модулей добавляются вместе с первым use case, а не заранее пустыми. Инициализированный каркас уже содержит конфигурацию, logger, Prisma, middleware, versioned API и health test.

Админ-панель и admin API не входят в это дерево MVP. Их будущая граница описана отдельно в post-MVP документе.

Заменяемые вычислительные и AI-модули

calculations обращается к движкам через registry/port по system + methodologyVersion. Временные нумерология, Human Design и Матрица v0 не импортируют Prisma/Express и могут быть заменены новой версией без миграции старых артефактов.

Доменный AI port не содержит названий поставщиков:

AiProvider.generateStructured(request, outputSchema) ->
  providerRequestId + provider + modelSnapshot + output + usage

Конкретный provider выбирается конфигурацией worker. Provider/model snapshot хранится в GenerationJob и InterpretationArtifact, поэтому два результата остаются воспроизводимыми даже после смены модели.

Технологии и версии на 2026-09-03

Назначение Выбор
Runtime Node.js 24+
HTTP Express 5.2.1
Язык TypeScript 7.0.2
БД PostgreSQL 18, Prisma Client/adapter 7.10.0
Логи Winston 3.19.0, JSON
Эфемериды Swiss Ephemeris 2.10.03 через sweph 2.10.3-8
Dev reload ts-node-dev 2.0.0 — как в референсе
Quality Oxlint 1.81.0 и Oxfmt 0.66.0 вместо ESLint/Prettier
Тесты встроенный node:test + Supertest 7.2.2

Prisma CLI зафиксирован на последней взаимно совместимой стабильной линии 7.10.0: npm-тег latest на дату проверки указывал на release candidate 8.0.0, тогда как стабильный @prisma/client оставался 7.10.0. Бизнес-зависимости (YooKassa SDK, AI SDK и Resend) не добавлены до первого use case; AI SDK остаётся только внутри конкретного adapter.

Полный development tree Prisma 7.10.0 временно даёт npm audit warnings из CLI/optional цепочки deepmerge-ts/mysql2. Production-слой удаляет dev и optional пакеты после генерации клиента; получившийся runtime проверен отдельно и имеет 0 известных audit-уязвимостей на дату документа. Нельзя делать audit fix --force и непроверенный major override: обновление Prisma выполняется всей совместимой линией после stable-релиза.

Фоновые задачи без преждевременной инфраструктуры

Для объёма пилота достаточно PostgreSQL job/outbox таблиц и отдельного процесса worker:

  • API в одной транзакции меняет доменную запись и создаёт job/outbox event;
  • worker забирает записи через FOR UPDATE SKIP LOCKED, продлевает lease и делает retry с backoff;
  • уникальный idempotency key предотвращает дубль эффекта;
  • после нескольких ошибок запись уходит в dead; причина видна в технических логах, retry выполняется безопасной операционной командой/скриптом с тем же idempotency key;
  • позднее этот контракт можно перевести на очередь, не меняя services.

Расчёт Swiss Ephemeris можно выполнять синхронно только после измерения latency; AI, уведомления, renewals и финансовая сверка всегда асинхронны.

API и ошибки

  • Base path: /api/v1; liveness: GET /api/v1/health.
  • Успех возвращает { "data": ... }, ошибка — { "error": { "code", "message", "details", "requestId" } }.
  • Клиентский x-request-id ограничивается длиной, иначе создаётся UUID; id возвращается в заголовке.
  • Pagination — cursor-based для событий/платежей/логов, page-based допустима для малого каталога.
  • Контракт после утверждения оформляется OpenAPI; JSON-ввод валидируется на HTTP-границе.

Безопасность и данные

  • passwordless OTP через Resend: hash/HMAC кода, TTL 10 минут, максимум 5 попыток и rate limit по e-mail/IP;
  • после OTP — короткоживущий access token и rotating refresh session в HttpOnly Secure cookie;
  • birth data, координаты, цели и тексты консультаций считаются чувствительными ПДн;
  • логи содержат route/status/duration/requestId, но не request body, токены, e-mail и данные рождения;
  • шифрование транспорта, секреты только из secret storage, backup/restore drill;
  • RBAC дополняется object-level проверкой: практик видит клиента только в разрешённом контексте консультации;
  • финансовые state transitions и provider callbacks создают audit/event trail без административного CRUD.

Юридические формулировки и сроки хранения должны быть утверждены специалистом; документация проекта не является юридической консультацией.