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

ADR-0007: Database Schema Design

Статус

Accepted

Контекст

Финансовая система требует надёжной, нормализованной схемы базы данных, которая: - Обеспечивает ACID свойства для финансовых операций - Поддерживает аудит всех изменений состояния - Разделяет пользователей и их кошельки (многие ко многим) - Предотвращает double-spending и race conditions - Обеспечивает быструю выборку транзакций (балансы рассчитываются динамически) - Поддерживает различные типы операций (deposits, withdrawals, investments)

Решение

Нормализованная схема PostgreSQL с чётким разделением responsibility и строгими constraint.

Ключевые таблицы:

  1. users - Основная таблица пользователей

    CREATE TABLE users (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        email VARCHAR(255) UNIQUE NOT NULL,
        status VARCHAR(50) NOT NULL DEFAULT 'active',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    -- НЕТ поля wallet_address! Связь через отдельную таблицу
    

  2. wallets - Унифицированная таблица кошельков (заменяет user_wallet_addresses)

    CREATE TABLE wallets (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        user_id UUID REFERENCES users(id) ON DELETE CASCADE,
        address VARCHAR(42) NOT NULL UNIQUE,
        network_id INTEGER NOT NULL DEFAULT 1337, -- Saga VPS production по умолчанию
        type VARCHAR(20) NOT NULL DEFAULT 'deposit', -- deposit, personal, master, hot, cold, trading
        status VARCHAR(20) NOT NULL DEFAULT 'active',
        -- HD Wallet fields
        derivation_path VARCHAR(255), -- HD wallet derivation path (m/44'/60'/0'/0/index)
        address_index INTEGER, -- HD wallet derivation index
        is_hd_generated BOOLEAN NOT NULL DEFAULT false,
        is_primary BOOLEAN NOT NULL DEFAULT false,
        created_at TIMESTAMP NOT NULL DEFAULT NOW(),
        updated_at TIMESTAMP NOT NULL DEFAULT NOW()
    );
    

  3. transactions - ВСЕ финансовые операции

    CREATE TABLE transactions (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        user_id UUID REFERENCES users(id) ON DELETE CASCADE,
        type VARCHAR(20) NOT NULL, -- 'deposit', 'withdrawal', 'investment' 
        amount DECIMAL(20,8) NOT NULL CHECK (amount > 0),
        currency VARCHAR(10) NOT NULL DEFAULT 'USDC',
        status VARCHAR(20) NOT NULL DEFAULT 'pending',
        blockchain_tx_hash VARCHAR(66),
        from_address VARCHAR(42),
        to_address VARCHAR(42),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    

  4. investments - Инвестиционные позиции

    CREATE TABLE investments (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        user_id UUID REFERENCES users(id) ON DELETE CASCADE,
        strategy_name VARCHAR(100) NOT NULL,
        amount DECIMAL(20,8) NOT NULL CHECK (amount > 0),
        currency VARCHAR(10) NOT NULL DEFAULT 'USDC',
        status VARCHAR(20) NOT NULL DEFAULT 'active',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    

Альтернативы

Денормализованная схема с wallet_address в users

Отклонена - не поддерживает множественные кошельки на пользователя, усложняет HD Wallet архитектуру.

NoSQL база данных (MongoDB, DynamoDB)

Отклонена - отсутствие ACID транзакций, сложность поддержания консистентности финансовых данных.

Единая таблица для всех операций (Event Sourcing)

Рассматривалась - избыточная сложность для текущих задач, сложность queries для балансов.

Отдельные таблицы для каждого типа транзакций

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

Последствия

Положительные

  • ACID свойства для всех финансовых операций
  • Чёткое разделение данных по назначению
  • Поддержка множественных кошельков на пользователя
  • Простота добавления новых типов операций
  • Эффективные индексы для быстрых queries
  • Полная audit trail всех операций

Отрицательные

  • Необходимость JOIN операций для получения связанных данных
  • Дополнительная сложность схемы по сравнению с денормализованной
  • Необходимость поддержания referential integrity

Нейтральные

  • Использование UUID вместо auto-increment ID (лучше для distributed systems)
  • Необходимость осторожного проектирования миграций

Связанные решения

  • Связано с: ADR-0001 (Web3 Wallet Authentication) - для связи users ↔ wallet_addresses
  • Связано с: ADR-0004 (HD Wallet Architecture) - для генерации deposit addresses
  • Связано с: ADR-0003 (No-Mock Testing Architecture) - для тестирования с реальной БД

Примечания

Принципы проектирования:

  1. Нормализация: Каждая сущность в своей таблице
  2. Constraint: Строгие проверки на уровне БД
  3. Audit: Поля created_at/updated_at везде
  4. Consistency: DECIMAL для денежных значений (никогда FLOAT)
  5. Security: UUID как primary keys (не последовательные)

Важные constraint:

  • Все денежные amount > 0
  • Все балансы >= 0
  • Уникальность wallet_address
  • Один deposit адрес на пользователя
  • Cascade delete для сохранения integrity

Индексы для производительности:

-- Быстрый поиск по wallet адресу
CREATE INDEX idx_wallets_address ON wallets(address);
CREATE INDEX idx_wallets_user_id ON wallets(user_id);
CREATE INDEX idx_wallets_network_id ON wallets(network_id);

-- HD Wallet специфичные индексы
CREATE INDEX idx_wallets_hd_network_id_index ON wallets(network_id, address_index) WHERE is_hd_generated = true;

-- Быстрый поиск транзакций пользователя
CREATE INDEX idx_transactions_user_id ON transactions(user_id);
CREATE INDEX idx_transactions_status ON transactions(status);

-- Быстрый поиск инвестиций
CREATE INDEX idx_investments_user_id ON investments(user_id);
CREATE INDEX idx_investments_status ON investments(status);

Типы данных:

  • UUID - для всех ID (безопасность, уникальность)
  • DECIMAL(20,8) - для денежных сумм (точность без floating point ошибок)
  • VARCHAR с ограничениями - для строк с валидацией
  • TIMESTAMP - для всех временных меток

Миграции:

  • Все изменения схемы только через миграции
  • Никаких прямых изменений в psql
  • Backward compatible миграции где возможно
  • Тестирование миграций на копии production данных

Безопасность:

  • Никаких админских таблиц (адреса в конфигурации)
  • Row-level security через user_id
  • Регулярные backup процедуры
  • Валидация данных на уровне БД и приложения

Связи между таблицами:

users 1:N wallets (заменяет user_wallet_addresses)
users 1:N transactions (единый источник истины для балансов)
users 1:N investments

Критическое правило: НИКОГДА не изменять схему вручную. Всегда создавать миграции для любых изменений структуры БД.