Перейти к основному содержимому

Storage Layer — Модель хранения и жизненный цикл данных

Версия: 2.0
Дата: 23.04.2026
Статус: Готов к обсуждению

Назначение документа

Этот документ фиксирует не просто идею “PostgreSQL + Redis”, а полную модель хранения платформы vitrip.store с точки зрения:

  • уровней истины;
  • жизненного цикла данных;
  • границ между persistent state и volatile state;
  • правил восстановления, инвалидации и пересборки;
  • связи хранения с платформенными доменами;
  • требований промышленной эксплуатации.

Если Database Schema — Каноническая модель хранения платформы отвечает на вопрос “что должно быть закреплено как persistent model”, то этот документ отвечает на вопрос:

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

Опорные документы

Этот документ нужно читать вместе с:

Что Исправляет Новый Подход

Старый документ слишком рано редуцировал storage layer к простой схеме:

  • PostgreSQL = persistent;
  • Redis = volatile.

Для промышленной платформы этого недостаточно.

Потому что на самом деле нужно различать:

  • canonical persistent truth;
  • supplier-derived persistent trace;
  • operational offer state;
  • quote-time state;
  • booking transactional state;
  • governance and lineage state;
  • rebuildable cache;
  • ephemeral technical state.

Именно эта многослойность определяет, как должна жить большая платформа в реальности.

Что Второй Несущий Круг Требует От Storage Layer

После появления второго круга документов storage.md больше не может ограничиваться объяснением, где лежат persistent данные, а где лежат caches.

Теперь storage layer обязан явно удерживать:

Практический вывод:

storage layer должен описывать не только место хранения, но и policy жизни данных для каждой доменной реальности платформы.

Главный Принцип Хранения

Платформа не должна хранить данные только по признаку “это удобно держать в Postgres” или “это быстро положить в Redis”.

Она должна хранить данные по следующим вопросам:

  1. Это источник истины или производная проекция?
  2. Это долговременный факт или краткоживущая operational реальность?
  3. Это нужно для аудита или можно безопасно пересобрать?
  4. Это состояние домена или технический кэш?
  5. Это должно пережить сбой, rollback и расследование или нет?

Только после этого выбирается конкретный storage class.

Уровни Хранения Данных

1. Canonical Persistent Layer

Это слой стабильной truth-модели платформы.

Здесь живут:

  • canonical properties;
  • canonical products;
  • organizations and users;
  • policies and settings;
  • governance outcomes;
  • long-lived commercial and identity state.

2. Supplier Trace Layer

Это persistent слой, но не canonical truth.

Здесь живут:

  • supplier payloads;
  • supplier property and product representations;
  • sync traces;
  • ingest snapshots;
  • mapping context;
  • replay and diagnostics data.

3. Operational Offer Layer

Это слой operational state, который может быть краткоживущим, но не всегда является disposable.

Здесь живут:

  • offers;
  • availability snapshots;
  • price snapshots;
  • quote sessions;
  • quotes;
  • revalidation outcomes.

Часть этого слоя может иметь TTL-семантику, но всё равно должна храниться достаточно долго для объяснения решений и поддержки перехода от поиска к booking.

4. Transactional Booking Layer

Это слой бизнес-критичной истории и состояний.

Здесь живут:

  • bookings;
  • booking events;
  • payment events;
  • refunds;
  • amendments;
  • settlement-relevant events.

Этот слой должен переживать:

  • инциденты;
  • расследование;
  • reconciliation;
  • спорные кейсы;
  • финансовую отчётность.

5. Governance And Review Layer

Это persistent слой управляемости данных.

Здесь живут:

  • lineage;
  • source precedence;
  • merge decisions;
  • anomalies;
  • review cases;
  • manual locks;
  • adjudication outcomes.

6. Composition And Proposal Layer

Это persistent слой для управляемой композиции и presentation-grade фиксации составного продукта.

Здесь живут:

  • tour drafts;
  • draft items and alternatives;
  • proposals;
  • proposal versions;
  • proposal artifacts metadata;
  • ownership and publication context.

7. Rebuildable Cache Layer

Это слой данных, которые важны для производительности, но не являются первичным truth.

Сюда входят:

  • search result caches;
  • availability projections;
  • precomputed offer slices;
  • derived price projections;
  • API throttling counters;
  • response fragment caches.

8. Ephemeral Runtime Layer

Это слой технических временных состояний.

Сюда входят:

  • locks;
  • temporary rate windows;
  • transient orchestration markers;
  • short-lived session fragments;
  • runtime idempotency windows;
  • per-request technical memoization.

Классы Данных Платформы

Ниже — практическая классификация данных для всей системы.

Класс A. Нельзя терять

Это данные, которые являются system-of-record или audit-critical:

  • canonical entities;
  • users and organizations;
  • tenants, workspaces, role assignments and API clients;
  • governance decisions;
  • governance review cases and manual locks;
  • bookings and booking events;
  • quotes, если они стали коммерчески значимым обязательством платформы;
  • payments and refunds;
  • proposal versions and proposal artifacts metadata, если они уже стали рабочими бизнес-артефактами;
  • settlement-relevant traces.

Класс B. Нежелательно терять, но можно восстановить частично

Это данные, которые желательно хранить persistent enough:

  • supplier payload history;
  • quote sessions;
  • quote snapshots, ещё не ставшие окончательным transactional truth;
  • offer snapshots, которые участвуют в коммерчески значимом переходе;
  • revalidation traces;
  • anomaly queues;
  • review cases.

Класс C. Можно безопасно пересобрать

Это данные, которые можно регенерировать при корректной архитектуре:

  • search result caches;
  • non-audited projections;
  • precomputed ranking slices;
  • transient feed aggregation buffers;
  • cache mirrors derived from persistent source.

Класс D. Чисто runtime

Это данные, которые не обязаны переживать рестарт:

  • volatile locks;
  • request debouncing windows;
  • in-flight rate windows;
  • short-lived background markers;
  • local per-worker memoization.

Storage Classes Платформы

Платформа должна мыслить не двумя, а несколькими storage classes.

1. Primary Persistent Store

Основной кандидат: PostgreSQL / PostGIS.

Подходит для:

  • canonical domain data;
  • transactional truth;
  • governance truth;
  • quote and booking-supporting state, если он должен быть аудитируемым;
  • organization and identity model.

Отдельно важно: именно здесь должны жить tenant boundary, workspace ownership, role/capability assignment и audit-grade actor attribution.

2. Operational Persistent-But-Volatile Store

Тоже может быть PostgreSQL, но логически это другой класс.

Подходит для:

  • offer snapshots;
  • revalidation traces;
  • quote sessions;
  • actor-aware quotes;
  • supplier import snapshots, которые нужны для диагностики;
  • временно значимые operational records.

3. High-Speed Rebuildable Cache

Основной кандидат: Redis.

Подходит для:

  • hot availability projections;
  • hot price projections;
  • search results;
  • aggressively reused filtered slices;
  • fast counters;
  • low-latency short-lived coordination.

4. Object / Blob Storage

Нужен для:

  • images;
  • generated PDFs;
  • exported proposals;
  • proposal artifacts;
  • raw archived large supplier artifacts;
  • attachments для operational workflows.

5. Analytical / Reporting Layer

На текущем этапе не обязательно является отдельной технологией, но как storage class должен мыслиться отдельно.

Он нужен для:

  • heavy reporting;
  • financial analytics;
  • operational metrics history;
  • longitudinal quality analysis;
  • pipeline productivity and anomaly analytics.

Почему PostgreSQL И Redis — Недостаточно Как Формулировка

Фраза “PostgreSQL для persistent, Redis для cache” слишком слабая для нашей платформы, потому что:

  • часть operational state должна жить дольше, чем обычный cache;
  • часть quote/offer state должна переживать расследование и переход к booking;
  • часть данных в Redis не просто cache, а runtime control state;
  • часть данных в PostgreSQL не canonical truth, а supplier trace;
  • часть persistent state нужна ради governance, а не ради OLTP напрямую.

Поэтому storage layer должен описываться через данные, truth и lifecycle, а не только через технологии.

Storage Policy По Доменным Различиям

После фиксации второго круга платформе уже недостаточно storage policy уровня “эта таблица лежит в Postgres”.

Нужно различать как минимум следующие policy-вопросы:

1. Truth Policy

  • является ли запись canonical truth;
  • является ли она supplier trace;
  • является ли она actor-specific фиксацией;
  • является ли она transactional fact;
  • является ли она governance decision.

2. Freshness Policy

  • должна ли запись считаться live;
  • может ли она устареть без удаления;
  • требует ли она обязательной revalidation;
  • допускается ли показ stale representation.

3. Retention Policy

  • обязана ли запись храниться ради аудита;
  • может ли быть архивирована;
  • может ли быть удалена после retention window;
  • можно ли её материализовать повторно из более фундаментального слоя.

4. Isolation Policy

  • должна ли запись быть tenant-scoped;
  • должна ли она быть workspace-scoped;
  • допускает ли она partner/agency visibility overlays;
  • должна ли она иметь actor-aware access trace.

5. Publication Policy

  • можно ли её публиковать во внешние surfaces;
  • требует ли она governance clearance;
  • может ли она жить только во внутреннем operational контуре.

6. Commercial Accountability Policy

  • фиксирует ли запись коммерческое обещание;
  • нужна ли она для объяснения monetary breakdown;
  • участвует ли она в repricing history;
  • нужна ли она для downstream settlement и reconciliation;
  • должна ли переживать потерю cache и runtime state.

Persistent Model И Storage Layer: Как Они Связаны

Ниже — практическая связка с database-schema.md.

support

Живёт в primary persistent store.

Характеристики:

  • low churn;
  • long-lived;
  • often referenced;
  • critical for consistency across domains.

core

Живёт в primary persistent store.

Характеристики:

  • canonical truth;
  • moderate write intensity;
  • heavy read usage;
  • requires indexing, geo-search and textual lookup.

supplier

Живёт в persistent trace layer.

Характеристики:

  • high write intensity;
  • append-heavy patterns;
  • replay and diagnostics needs;
  • may require retention policy and tiering.

offer

Живёт частично в persistent operational layer и частично в rebuildable cache layer.

Это одна из самых чувствительных зон storage design.

Что должно жить persistent

  • quote sessions;
  • quotes;
  • quote repricing traces;
  • revalidation outcomes;
  • offer states, которые стали основанием для пользовательских или коммерческих действий;
  • cross-step continuity между search, quote и booking.

Что должно быть actor-aware

  • quotes;
  • quoted price views;
  • quote validity windows;
  • applied commercial policy traces;
  • access-sensitive pricing projections, если они становятся основанием для коммерческого действия.

Что может жить как rebuildable cache

  • hot search projections;
  • non-audited result ranking slices;
  • ephemeral availability mirrors;
  • short-lived price caches.

booking

Живёт только в transactional persistent layer.

Redis может помогать booking path, но не должен становиться источником booking truth.

identity

Живёт в primary persistent store.

При этом часть runtime access and throttling state может отражаться в Redis.

Но storage layer обязан различать:

  • identity truth;
  • organization truth;
  • tenant isolation boundary;
  • workspace-scoped working state;
  • runtime access windows and throttling markers.

governance

Живёт в persistent store.

Нельзя уводить lineage, review cases, manual locks и anomaly resolution в чистый cache или неустойчивые очереди.

tour

Основные draft / proposal / version entities должны жить persistent.

Proposal artifacts как blobs могут жить в object storage, но их metadata, ownership, version link и publication state должны жить persistent.

Derived presentation fragments могут кэшироваться.

Offer Storage Logic

Offer layer требует особенно аккуратного хранения.

Что важно

Offer — это не навсегда стабильная сущность, но и не одноразовый JSON ответа.

Для промышленной платформы нужно различать:

  • searchable offer projection;
  • quote-basis offer snapshot;
  • booking-basis offer snapshot;
  • actor-specific quote fixation.

Практическая логика

Searchable Offer Projection

Может:

  • кэшироваться;
  • иметь короткий TTL;
  • пересобираться;
  • не хранить полную audit-grade историю.

Quote-Basis Snapshot

Должен:

  • быть воспроизводимым;
  • иметь validity window;
  • хранить важные семантики цены, availability и policy;
  • поддерживать переход к revalidation.

Actor-Specific Quote Fixation

Должен:

  • быть привязан к tenant / workspace / actor context;
  • хранить price view, показанный именно этому контексту;
  • хранить monetary breakdown и policy basis, если они стали частью коммерческого обещания;
  • переживать потерю Redis и cache invalidation;
  • объяснять, почему именно такой quote был создан и кем;
  • оставлять trace к последующему booking или к утрате актуальности.

Repricing Trace

Должен:

  • жить persistent enough для audit и support;
  • различать simple revalidation и actual repricing;
  • хранить причину денежного изменения;
  • хранить связь между старым и новым quoted promise;
  • быть пригодным для dispute resolution и operator explanation.

Booking-Basis Snapshot

Должен:

  • быть неизменяемой основой booking attempt;
  • храниться достаточно надёжно для аудита и расследования;
  • позволять понять, что именно было подтверждено или пыталось подтверждаться.

Redis: Что Там Должно Жить

Redis полезен платформе не потому, что он “быстрый вообще”, а потому что он хорош для конкретных типов state.

Подходящие категории

  • search result caches;
  • hot price projections;
  • hot availability projections;
  • API rate windows;
  • distributed short-lived locks;
  • idempotency windows;
  • short-lived orchestrator markers;
  • user-facing transient session context.

Что не должно считаться Redis-truth

  • canonical property and product data;
  • final commercial price truth;
  • booking state;
  • governance decisions;
  • organization identity model;
  • audited quote basis.
  • monetary breakdown, ставший частью quoted promise;
  • settlement-relevant trace.

PostgreSQL: Что Там Должно Жить

PostgreSQL нужен не потому, что это “основная БД”, а потому что он соответствует потребностям платформы:

  • ACID transaction semantics;
  • rich relational model;
  • JSONB for controlled flexibility;
  • PostGIS for geo layer;
  • indexing and partitioning support;
  • event and audit persistence;
  • strong queryability for operational and reporting needs.

Там должны жить

  • canonical entities;
  • supplier trace layer;
  • offers where persistence is required;
  • quote sessions;
  • quotes where actor-specific fixation matters;
  • quote repricing history and policy traces;
  • bookings and financial traces;
  • governance layer;
  • identity and organizations;
  • tour drafts and proposals;
  • operational audit events.

Object Storage: Что Туда Выносить

Промышленная платформа должна рано отделить blob content от operational relational truth.

В object storage должны жить

  • hotel images;
  • room/product images;
  • proposal PDFs;
  • proposal artifacts and branded exports;
  • generated exports;
  • archived source files;
  • bulky supplier snapshots, если они не нужны как queryable relational rows.

В БД должны оставаться

  • metadata;
  • references;
  • provenance;
  • ownership;
  • generation state;
  • processing outcomes.

TTL И Retention Policy

Важно различать TTL и retention.

TTL

TTL нужен для:

  • cache entries;
  • short-lived offer projections;
  • temporary counters;
  • runtime locks;
  • technical session fragments.

Retention

Retention нужен для:

  • supplier snapshots;
  • quote sessions;
  • quotes;
  • quote repricing history;
  • booking event history;
  • governance history;
  • proposal versions and proposal artifacts metadata;
  • anomaly logs;
  • settlement-relevant traces.

Ошибка, которой нужно избежать

Нельзя описывать retention-critical данные как просто “данные с TTL 15 минут”, если они участвуют в коммерчески значимом переходе или расследовании.

Commercial Storage Logic

После фиксации Commercial Model — Коммерческая модель, цена, settlement и канальные условия storage layer должен различать не только технические классы данных, но и коммерческие уровни жизни записей.

Что Нужно Различать

  • indicative price projection;
  • actor-aware quoted promise;
  • repricing trail;
  • booking-time commercial basis;
  • settlement-relevant financial trace.

Indicative Price Projection

Может:

  • кэшироваться;
  • иметь короткий TTL;
  • пересобираться из более надёжных слоёв;
  • не хранить полный audit-grade breakdown.

Actor-Aware Quoted Promise

Должен:

  • жить persistent;
  • переживать потерю Redis;
  • хранить validity window;
  • хранить monetary breakdown и policy context в достаточном объёме;
  • быть доступным для support, audit и safe booking transition.

Repricing Trail

Должен:

  • жить дольше, чем сам hot cache;
  • объяснять, почему quote изменился;
  • связывать старую и новую commercial fixation;
  • быть пригодным для dispute and support workflows.

Booking-Time Commercial Basis

Должен:

  • переживать booking lifecycle;
  • удерживать связь с quote context;
  • позволять отличить исходный quote от later settlement reality;
  • быть доступным для rollback, investigation и amendment logic.

Settlement-Relevant Financial Trace

Должен:

  • храниться как retention-grade persistent truth;
  • не зависеть от существования кэшей или transient quote sessions;
  • быть пригодным для reconciliation, reporting и refund economics;
  • сохранять связь с policy basis и booking lifecycle.

Invalidation And Rebuild Strategy

Большая платформа должна знать не только где хранить данные, но и как:

  • инвалидировать их;
  • восстанавливать;
  • пересчитывать;
  • синхронизировать.

Основные стратегии

1. Cache Invalidation By Event

Подходит для:

  • изменения supplier-derived availability;
  • изменения цены;
  • обновления property-content;
  • изменения partner policy affecting projections;
  • изменения tenant-specific commercial rules affecting visible price views.

2. Rebuild By Request

Подходит для:

  • on-demand regeneration search result caches;
  • regeneration of ranking slices;
  • lazy rebuild after cache expiry.

3. Scheduled Refresh

Подходит для:

  • hot commercial datasets;
  • near-term availability and price windows;
  • high-value search corridors.

4. Explicit Revalidation

Подходит для:

  • quote to booking transition;
  • reopening saved proposal;
  • reusing older TourDraft components;
  • refreshing stale but potentially actionable offers.

5. Governance-Aware Publication Refresh

Подходит для:

  • применения manual governance decision;
  • снятия или появления manual lock;
  • подтверждения review case;
  • изменения source precedence, влияющего на downstream published state.

Failure And Recovery View

Storage layer должен быть спроектирован не только под normal path, но и под failure path.

Платформа должна переживать

  • Redis loss;
  • partial supplier inconsistency;
  • replay after ingestion failure;
  • booking state investigation after timeout;
  • quote state recovery;
  • background job duplication;
  • delayed invalidation;
  • stale cached projections.

Что это означает

Redis loss не должен означать потерю критического truth.

PostgreSQL outage не должен приводить к логическому смешению cache data и permanent state.

Ingestion replay не должен ломать governance traceability.

Booking recovery не должен зависеть от существования только одного последнего cache key.

Quote recovery не должен зависеть от сохранившегося transient search cache.

Repricing history recovery не должна зависеть от существования только одного текущего Quote.

Settlement trace recovery не должна зависеть от повторного вычисления старой price logic “задним числом”.

Tour proposal recovery не должен требовать существования только blob-файла без metadata и version link.

Масштабирование Storage Layer

PostgreSQL Scaling Direction

  • read replicas for reporting and heavy read paths;
  • partitioning for event-heavy and time-heavy domains;
  • dedicated indexes by domain access patterns;
  • possible future separation by operational load contours, not by arbitrary early sharding.

Redis Scaling Direction

  • separation by cache classes;
  • memory budgeting by state criticality;
  • eviction policy tuning by data class;
  • optional cluster mode for hot projections and counters.

Blob / Asset Scaling Direction

  • external object storage;
  • CDN for static distribution;
  • metadata in DB, content in blob layer.

Метрики Хранения, Которые Реально Нужны Платформе

Persistent Layer

  • growth by schema/domain;
  • write/read patterns by contour;
  • query latency by domain path;
  • partition pressure;
  • quote and booking state volume;
  • quote repricing volume and retention pressure;
  • governance queue growth;
  • supplier trace ingestion volume;
  • proposal/version/artifact growth by lifecycle stage;
  • tenant/workspace distribution of operational state.

Cache Layer

  • hit ratio by cache class;
  • invalidation rate;
  • rebuild frequency;
  • stale response incidents;
  • key churn;
  • memory pressure by namespace.

Cross-Layer Metrics

  • cache rebuild time after invalidation;
  • quote-to-booking revalidation failures;
  • mismatch between cached and revalidated price;
  • mismatch between cached and live availability;
  • time-to-recover after supplier or cache incident.

Что Должно Быть Перепроверено После Этого Документа

После стабилизации этой модели хранения нужно будет синхронизировать:

Роль Старых Черновиков

Предыдущая версия этого документа сохранена в:

Она не является актуальной storage truth, но может оставаться полезной как архив ранних технических идей, TTL-примеров и operational heuristics.

Текущий Практический Вывод

Storage layer платформы должен проектироваться так:

  • не как “одна БД и один кэш”;
  • не как набор случайных TTL;
  • не как техническая инфраструктурная заметка;
  • а как управляемая модель жизни данных платформы: что является truth, что является trace, что является operational state, что является transactional fact, что является rebuildable cache, что является purely runtime data.

Если удержать именно эту рамку, то платформа сможет масштабироваться без постоянного смешения смыслов между inventory, offers, booking, governance, identity и operational caching.

Уточнение под Фазы 4–6 (28.04.2026) — связь с DR tiers, isolation, security, retention

Документ опубликован 23.04.2026 (Фаза 3). После Фаз 4–6 storage layer каноничен интегрирован с DR posture, isolation, security; эта секция фиксирует связи.

Связь с DR — 4 каноничных recovery tiers

operations/disaster-recovery-and-capacity.md (Фаза 6) — 4 каноничных storage tier:

TierRTORPOStorage classes этого документа
Tier 160 минут5 минутTransactional truth (Booking, Payment, Settlement, Audit log, User/Tenant); Финансовая отчётность
Tier 24 часа30 минутOperational truth (Offer, Quote, Search projection); Tenant configurations; Notification queue
Tier 324 часа1 часAnalytical (DWH events stream, ML feature store); Logs
Tier 47 дней24 часаArchived (financial > 7 лет, audit > 2 лет, archived media)

При создании каждой новой entity — выбрать tier явно.

Связь с уровнями тенантной изоляции

reference/multi-tenant-isolation-strength.md (Фаза 5) — storage реализация per isolation level:

  • logical — partitioned by tenant_id + RLS policies в shared cluster;
  • dedicated_compute — отдельный schema или namespace per tenant в shared cluster;
  • dedicated_infrastructure — отдельный database instance / cluster per Enterprise tenant.

IsolationBoundaryCheck (continuous automated) — runs queries для validation.

Связь с security architecture

reference/security-architecture.md (Фаза 10) — storage encryption:

  • Encryption at-rest — TDE через cloud provider managed encryption + column-level encryption для sensitive PII;
  • Per-tenant keys для Enterprise (CMK — Customer-Managed Keys);
  • Backup encryption — managed keys + cross-region replication encrypted;
  • Audit log — отдельный WORM-grade storage class, immutable, 7 лет.

Связь с retention policies

reference/compliance-and-legal.md (Фаза 4) определяет regulatory retention:

Класс данныхRetentionCompliance basis
Financial records7 летEU TOMS, tax laws
Audit log (security/access)7 летSOC 2, GDPR
Personal data — booking-related7 лет (financial), then deletedGDPR + financial regulations
Personal data — marketingдо consent withdrawal + 3 годаGDPR + ePrivacy
Analytical events (anonymized)2 годаGDPR data minimization
Application logs90 днейoperational

Каждый поле в каноничной модели имеет attribute retention_policy (см. compliance-and-legal.md, data_field_inventory).

Связь с booking-state-machine

reference/booking-state-machine.md (Фаза 5) — booking storage особенности:

  • 14 каноничных состояний — все persistent в Tier 1;
  • booking_state_transition log — append-only, immutable;
  • unknown_external_state — requires special storage isolation для governance review.

Связь с расширением database-schema

reference/database-schema.md — расширение schema под Фазы 4–6 уже зафиксировано там в секции «Расширение схемы под Фазы 4–6 каноничной архитектуры». Storage-tier mapping для всех новых сущностей — в database-schema.md.

Связь с relation-to-implementation-baseline

overview/relation-to-implementation-baseline.md — текущий storage в apivitianadb:

  • single-tenant сейчас (Долг 1 — нужен tenant_id migration);
  • events_themes смешивает domain + analytical events (Долг 8);
  • нет formal Tier 1/2/3/4 разделения yet — нужна реструктуризация при миграции.

Каноничный итог уточнения

Storage остаётся базовым layer хранения. Реализация:

Уточнение выполнено через no-destruction.