Архитектура
Подход
Модульная архитектура с направлением зависимостей внутрь: предметные правила → сценарии и порты → внешние адаптеры → точки сборки. Наличие TypeScript-интерфейса само по себе не делает код чистым: домен должен оставаться независимым от инфраструктуры и её форматов.
Базовый стек: TypeScript strict, Node.js 24 LTS, npm workspaces. Для HTTP используется Fastify, для каталога и устойчивых completion jobs — PostgreSQL. Консоль написана на TypeScript и обращается через SDK; файловые шлюзы Nginx и конкретный HA blob-store ещё не реализованы.
Направление зависимостей
| Модуль | Разрешённые внутренние зависимости | Что запрещено |
|---|---|---|
| domain | Нет | HTTP, БД, Node/browser globals, внешние runtime-библиотеки |
| application | domain | Конкретные адаптеры, transport DTO, глобальная конфигурация |
| contracts | Нет | Домен, application, storage и HTTP server framework |
| infrastructure | application, domain | Приложения, UI и SDK |
| sdk | contracts | Серверные пакеты и Node-only API |
| apps/api | application, domain, contracts, infrastructure | Другие apps и SDK |
| apps/worker | application, domain, infrastructure | HTTP/Web/SDK |
| apps/scheduler | application, domain, infrastructure | HTTP/Web/SDK |
| apps/backup | application, domain, infrastructure | HTTP/Web/SDK и другие apps |
| apps/web | sdk, contracts | Все серверные пакеты и другие apps |
Импорты типов также соблюдают границы. Межпакетный доступ — по workspace-имени через src/index.ts; даже разрешённый соседний пакет не открывает все свои внутренние файлы. Граф проверяется dependency-cruiser. Для domain/application дополнительно запрещены runtime-зависимости на сторонние пакеты и Node builtins; import type не должен использоваться для обхода модели.
В contracts допускаются библиотеки runtime-схем после выбора подхода; в SDK — browser-compatible зависимости по необходимости. JSON/wire DTO не обязаны совпадать с domain objects. Преобразования на границах явные.
Ответственность слоёв
Domain. Идентичность пакета, политика версий, допустимость перехода состояния, правила меток и удаления. Детерминированные функции/объекты. Время и случайность передаются извне.
Application. PublishPackage, CompleteUpload, ResolveDownload, UpdateMetadata, ScheduleTransfer. Сценарии оркестрируют порты и проверяют права. Не держат SQL-транзакцию на протяжении сетевой передачи. Только необходимые интерфейсы, без заготовок на все будущие операции.
Infrastructure. Адаптер PostgreSQL, backend blob-store, хеширование/архивы, event outbox, чтение метрик, идентификаторы и часы. Порты должны явно описывать устойчивость записи, отмену и ошибки; реализации проходят общий набор контрактных тестов.
Composition roots. Apps валидируют конфигурацию, создают адаптеры и сценарии, регистрируют lifecycle/shutdown. HTTP handlers переводят запрос в типизированный сценарий и результат в ответ. Не следует переносить SQL и правила публикации в routes.
Содержимое и управление
PostgreSQL хранит каталог, права, ревизии, состояния загрузок/задач, outbox. Blob-store хранит исходные байты; API не читает файлы целиком в память. Шлюз раздаёт разрешённый неизменяемый blob. Внешние приложения не являются промежуточными узлами передачи или обязательными серверами авторизации.
LocalBlobStore — профиль одного сервера. HA использует проверенное отказоустойчивое хранилище с документированными гарантиями и доступные копии БД/входного адреса. Выбор схемы и провайдера оформляется ADR до обещаний о доступности.
Правила изменений
Расширение авторизации сервисов проектируется внутри существующих слоёв: stable service account, credentials, action/resource bindings и общий application authorizer для HTTP API и worker. Новые границы и миграция старых read/write описаны в ADR 0016; широкая модель остаётся proposed, первый рабочий профиль принят в ADR 0017: root bootstrap, точные repository actions, managed credentials и повторная проверка mutations перед commit. Карта API отделяет работающие маршруты от планируемых.
- Новый пакет создаётся только с конкретной ролью, владельцем правил и разрешёнными зависимостями.
- Общее поведение извлекается после появления реального повторения; не создаём универсальный framework заранее.
- Новые брокер/БД/сервис требуют обоснования: какую измеренную проблему они решают и как восстанавливаются.
- Границы API, долговечность, операции удаления, топология HA и протокол очереди меняются через ADR и проверяемые сценарии.
- Размер каталога, потоков и очередей должен быть ограничен конфигурацией и доступной ёмкостью.
Текущее состояние
Резервирование B1 — отдельный composition root apps/backup (операторский CLI, не HTTP-сервис). Domain хранит состояния job/фаз, решение по повторному ключу, gate схемы и валидацию manifest; application — CaptureBackup, VerifyPoint, RestoreToEmptyTarget и узкие порты; infrastructure — PostgreSQL jobs/pins/барьер допуска unlink, snapshot-экспорт по реестру таблиц, файловый vault и загрузку restore. Online GC и offline repair удаляют содержимое только через общий допуск unlink. Решение: ADR 0054, протокол и следующие этапы: ADR 0037, BACKUP_RECOVERY. Reader pins по-прежнему не заменяют backup pins. B2 (ADR 0056): тот же composition root запускает долгоживущий агент (lease/fencing в БД); domain — чистые расписание, retention и предупреждения, application — BackupAgent, ApplyBackupRetention, BackupControl и порты очереди, плана, каталога и блокировки vault; infrastructure — PostgreSQL-хранилища и prune слиянием inventory; API лишь ставит типизированные задания и читает состояние из БД.
Фоновая физическая очистка реализована отдельными domain/application/SQL модулями, API/SDK и UI. Неблокирующие per-object guards защищают активные uploads и shared content readers; writer запускает ограниченные порции. Миграции 16/17, динамическая политика и отсутствие SQL-транзакций вокруг filesystem I/O: ADR 0035, эксплуатация.
В infrastructure добавлен BandwidthGovernor — локальный исполнитель upload/download бюджета и квоты принципала. API подключает его к HTTP-потокам; application/storage и worker не зависят от HTTP pacing. AdmissionQueue ограничивает также активные операции принципала. Контракт и граница одного процесса: ADR 0008.
Реализованы domain/application, нативный HTTP API, PostgreSQL с миграциями, LocalBlobStore, multipart/resume, каталог и история assets, SDK, консоль, completion worker и скачивание по ID артефакта, идентичности UPack и пути файла. Один API владеет standalone-БД. Распределённый scheduler и HA остаются будущими ролями. Рабочие контракты и ограничения: CORE_RUNBOOK, LIFECYCLE_AND_CATALOG, ADR 0006.
Учётные записи, группы и сессии добавлены в тех же слоях: domain проверяет идентификаторы и точные repository grants, application определяет сценарий/порт, infrastructure реализует PostgreSQL и хеширование. API проверяет сессию и собирает principal при каждом запросе; UI использует SDK. Порядок и группировка списка UPack задаются чистой функцией application. ADR 0011.
Профиль read gateways сохраняет существующие границы: composition root API выбирает роль и связывает PostgresDownloadLease с доступностью локального governor. Domain/application не зависят от leases раздачи. Writer остаётся один, readers используют тот же согласованный root; миграция 5 и mode/maintenance barriers защищают конфигурацию. ADR 0009, эксплуатация.
Контрактный guard composition root сверяет зарегистрированные HTTP routes с OpenAPI перед готовностью сервера. Metadata живёт в contracts и не исполняет ACL; авторизация остаётся в application/infrastructure. Спецификация экспортируется сборкой и не включается в браузерный bundle. ADR 0018.
Ограниченный delegation профиль принят в ADR 0019: точный operator key → target account, отдельные admin actions и ceiling, без цепочек. Application валидирует команду и передаёт Principal порту; infrastructure повторно разрешает authority в той же транзакции, где меняет grants/keys/policy и пишет audit. Domain хранит чистые типы/actions. Контракт и обновление: SERVICE_DELEGATION.
Файловые страницы выделены в небольшие application/infrastructure asset-page модули. Параметры и prefix bounds чистые, SQL seek и scope cursor находятся в адаптере; каталог/HTTP только вызывают сценарий. Новый API сохраняет прежний assets list. ADR 0020.
Discovery репозиториев — чистая application projection актуального Principal, не новая таблица/RepositoryStore. Coarse permission mapping вынесен из HTTP в application с прежними semantics. Domain добавляет repository.read, transport и SDK используют отдельный wire contract. ADR 0021.
Клиентская очередь скачиваний живёт в SDK: DownloadQueue управляет расписанием, checkpointedDownload использует узкий DownloadStorage и существующий HTTP-клиент. OPFS/выбор конечного файла принадлежат web; domain/application/server не зависят от браузерного storage. ADR 0022. Области и видимость API описываются contracts registry; application operationVisible проецирует применимость по действующим правам без HTTP/БД. API объединяет её с ролью шлюза и выдаёт advisory-каталог. SDK namespaces делегируют существующему транспорту. ADR 0023, контракт.
Именованные вложения используют отдельные domain rules, application AttachmentStore/BuildAttachments и PostgreSQL-адаптер. Байты проходят прежний transfer stack. Migration 12 хранит append-only snapshots и FK-пины targets. Web разделяет annotation editor, attachments controller и общий worker hash. ADR 0024.
Границы и топологический порядок сборки теперь задаёт config/architecture.json. Проверяемые лимиты 500/300/100 и временные исключения описаны в ADR 0027; команды, расширение и требования к комментариям — в ENGINEERING_GATES. Существующий долг и порядок декомпозиции: аудит 2026-09-25.
SDK использует композицию: публичный ArkvoryClient делегирует группам операций, которые используют общий узкий HttpPort. HTTP-авторизация, ошибки и bounded parsing централизованы; transfer workflows сохраняют отдельные retry/checksum/abort-инварианты. Прежние flat methods и namespaces защищены проверкой строгого внешнего TS consumer. ADR 0028.
Маршруты upload/session/parts/completion выделены в upload-routes; UploadReceiver владеет ожиданием входящих bytes и deadline, получая bandwidth governor и диагностику через узкие порты. Эти механизмы остаются в HTTP-адаптере. LocalBlobStore отдельно обеспечивает точную длину завершённого чтения, включая Range и части. ADR 0029.
Декомпозиция API composition root завершена: server связывает runtime, application factories, security/context, диагностику, фоновые задачи и feature registrars. Владение ресурсами и rollback централизованы в ApiRuntime; очереди и потоки закрываются до фоновой работы и pools. Регистраторы получают узкие зависимости; полная композиция не передаётся обработчикам. ServerConfig содержит webDirectory, окружение читается только loadConfig. ARCH-016/017 сняты без новых исключений. ADR 0030.
Управление установкой выделено в apps/deploy; единственная внутренняя зависимость — переносимый packages/contracts для протокола обновлений. UpdatePort отделяет переключение/откат от supervisor, GitHub и файловой системы. Привилегированный updater отделён от HTTP API защищённым mailbox: сервер принимает только check/configure/apply и читает статус. ADR 0031, ADR 0038, обновления.
Локальный мастер удалённой установки также принадлежит apps/deploy: HTTP session/формы, orchestration, SSH, команды платформ и туннель разделены. Клиентский пакет включает отдельный bundle; apps/cli и публичный API не получают SSH-credentials и не приобретают зависимость от deploy. Приватный туннель не является сетевой публикацией сервиса. ADR 0036.
Комплекты запуска делегируют тому же deploy CLI. Релизный pipeline разделяет read-only сборку/приёмку и привилегированную публикацию уже проверенных байтов; состав и хеши assets проверяются перед публикацией. ADR 0032.
Удалённый arkvoryctl находится в apps/cli, зависит только от SDK/contracts и не имеет доступа к внутренним слоям сервера. CLI владеет профилями и локальными чекпойнтами; протокол, авторизация и сетевые повторы остаются в SDK. Устанавливается отдельными клиентскими пакетами с private runtime. ADR 0034, справка.
Единая идентичность Arkvory и чистая дорелизная установка описаны в ADR 0040. Все имена конфигурации, SQL, сервисных ключей и локального состояния используют Arkvory; нормализатор альтернативных имён и зависимость worker от contracts удалены.
Восстановление download intents остаётся в web (OPFS/sessionStorage/Web Locks); SDK принимает paused jobs через явный restore. Staging сегментирован по 8 MiB. CLI packages publish оркестрирует upload/register с прежним receipt; metadata search реализован отдельным SQL-модулем через BrowseStore. ADR 0041.
Владение PostgreSQL-сессией выделено из каталога: StorageOwnership ограничивает период подтверждения locks, необратимо закрывает потерянное соединение и не допускает повторный claim. Это не межсерверный fencing и не репликация: ADR 0042.
Объектные locks uploads/GC и динамические content pins используют тот же инфраструктурный монитор SQL-сессии. PostgresJobLease отдельно связывает heartbeat/finish reservation с конечным локальным окном; worker отслеживает потери и ограничивает shutdown. ADR 0043.
Проект независимой репликации отделяет переносимое mirror с авторитетным primary от синхронного HA с внешним fencing. Ordered outbox, durable pins/receipts, bounded transfer, новый worker и отдельная gateway composition добавляются только вместе с первым рабочим сценарием. Пока это проект, без новых runtime ролей или HTTP маршрутов: ADR 0044, протокол, API, UI.
Наблюдаемость остаётся в infrastructure без внешних зависимостей: DiagnosticLogger (JSON-строки, уровни, поля процесса, неблокирующий учёт потерь) и MetricsRegistry (Prometheus text). Composition roots создают один логгер на процесс, ставят crash-only обработчики и регистрируют /health/metrics. Correlation ID передаётся явно копией Principal на запрос и сохраняется адаптерами задач и аудита; domain/application его не интерпретируют, глобального контекста нет: ADR 0052.