Архитектура 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.
Юридические формулировки и сроки хранения должны быть утверждены специалистом; документация проекта не является юридической консультацией.