Модернизация legacy-системы крупного речного оператора

Крупный оператор речных круизов (анонимно) Туризм и пассажирские перевозки 2024 — наст. время
211
SQL миграций
17 620
записей мигрировано
9
live PDF-шаблонов

Услуги

  • legacy-modernization
  • complex-systems
  • ai-development

Стек

  • SvelteKit 2
  • Svelte 5
  • Supabase Postgres
  • Hatchet
  • Gotenberg
  • PDFKit
  • Tailwind 4

Один из крупнейших операторов речных круизов с оборотом десятки тысяч заявок в сезон. Legacy PHP 5.4 + MariaDB монолит за 8+ лет накопил техдолга — невозможно добавлять новые фичи, всё ломается. Задача: миграция на современный стек без потери истории заявок и без downtime.

Контекст

Систему писали разные подрядчики с 2016 года. Накопились классические проблемы:

  • PHP 5.4 EOL — невозможно ставить современные библиотеки.
  • MariaDB без формальных миграций — изменения схемы делались вручную через phpMyAdmin.
  • Бизнес-логика в хранимых процедурах + в шаблонах — отслеживать изменения цен и скидок невозможно.
  • Платёжный модуль inline в шаблонах — копипаст между разделами.
  • 5 разных способов отправки уведомлений (mail(), SMTP, telegram-бот, sms-шлюз) без единой шины.
  • Нет тестов. Релиз = «попробовать в проде, откатить если что».

При этом система обрабатывала тысячи заявок в сезон, и просто переписать всё с нуля означало потерять 8 лет истории заявок, цен, договоров с агентствами.

Подход

Поэтапная миграция с сохранением старой системы в работе и постепенным переключением модулей.

  1. Migration_app — воспроизводимый pipeline. Extract из MariaDB → Transform (нормализация полей, исправление кодировок, выравнивание типов) → Insert в Supabase PG 17 → Verify (сравнение SQL-выборок legacy ↔ новая БД с диффом). Каждый прогон автоматически детектирует drift и предлагает фикс.
  2. Public_id 7-символьный для всех URL-сущностей вместо bigint. Backfill через триггеры BEFORE INSERT, без downtime. Короткие URL /orders/Xy3kR9a вместо /order.php?id=12345.
  3. Insert-only versioning через valid_from. Единая модель для discounts, prices, booking_rules, promotions, document templates. Старые версии неприкосновенны. Резолвер: max(valid_from) ≤ effective_date. Никаких race conditions при изменении цен.
  4. Universal Document Platform v2 (merged 2026-04-26, tag pf-pdf-foundation-v1). Hatchet workflows + Gotenberg HTML→PDF + изолированная schema hatchet в общем Supabase Postgres. Заменили самописный PDFKit-pipeline.
  5. Shared modules (shared-pay, shared-notifications, pay-service) для повторного использования между Круизным флотом и параллельным продуктом «Прогулочный флот».
  6. mTLS + HMAC bridge к остаточной legacy-системе для постепенной миграции — новый фронт ходит к старой PHP за остатками атомарных операций.

Результат

  • 211 SQL миграций в production без блокировок (используем CREATE INDEX CONCURRENTLY, ADD COLUMN ... DEFAULT NULL + батч-backfill, feature-flags для rollback).
  • 17 620 единиц данных мигрировано (10 578 круизов + 7 042 прогулок) с побитовой верификацией.
  • 312k платёжных записей скорректированы migration drift-fix процедурой за один сеанс (302 940 buh + 9 101 alfabank помечены site_id='pf').
  • 9 LIVE PDF-шаблонов в работе (1 voucher + 1 voucher_pf + 7 версий pf_excursion).
  • >1000 одновременных пользователей поддерживается (требование ТЗ).
  • 20+ ролей в системе (менеджер, бухгалтер, директор круиза, агент, менеджер агента, физлицо и др.) с разделением через RLS-политики.
  • Бизнес-эффект. Время добавления новой фичи: было 1-3 месяца → стало 1-2 недели. Релизы: было редкие и опасные → теперь несколько раз в неделю.

Что использовали

SvelteKit 2 + Svelte 5 runes + Tailwind 4 + shadcn-svelte (frontend), Supabase Postgres 17 + RLS + Edge Functions Deno (backend), Hatchet TS SDK v1 (workflow engine), Gotenberg 8 (HTML→PDF), Альфа-банк SBP C2B + ЮKassa (платежи), mTLS + HMAC + Idempotency (legacy bridge), Caddy auto-TLS, systemd, Docker Compose. AI-усиление: Claude Code SDK + Hermes Stack (9 docker-сервисов) для оперативного контроля проектов.

Похожая задача?

Расскажите контекст — подскажу, что и как делать.

Обсудить похожий проект →