контекст
Telegram-платформа с инкентивизированными заданиями: пользователи выполняют шаги в партнёрских проектах, модераторы валидируют выполнение, система раз в месяц распределяет бюджет по сложной скоринговой схеме (ранги 1–100, накопленные очки с мультипликатором 1.1x–2.5x, 15% реферальная программа). Выплаты — on-chain в USDT на Arbitrum, требование: ноль дублей выплат при retry/перезапуске воркера, идемпотентность всех операций. За 7 месяцев — рост с 25k до 63k MAU, 1M+ ручных проверок задач.
компромиссы
- Скоринг в PLpgSQL stored procedures vs application-level: вынес в БД — атомарность с записью лидерборда, минус сетевой round-trip; минус — сложнее юнит-тестировать, плюс — невозможно случайно посчитать половину.
- Batched on-chain payouts (раз в день) vs immediate per-claim: батч экономит газ ×10 и снижает шум блокчейна, но требует idempotency keys и persistent dedup-таблицы; выбрал batched после первой недели работы immediate-схемы.
- Mozilla Fluent vs gettext для i18n: Fluent даёт inline pluralization и DSL для биз-фраз; aiogram-i18n + Fluent — нативно асинхронно. .ftl-файлы в репо как текстовый source of truth.
- Jupyter-аналитика для месячных бюджетов вместо real-time дашборда: дешевле в поддержке (1 ноутбук vs Grafana stack), быстрее итерации формул при ежемесячной калибровке.
- docker-compose вместо k8s: 3 сервиса (bot / payout-worker / moderation-admin), один host — k8s overhead не оправдан.
архитектура
┌─────────────┐ aiogram ┌──────────────┐ stored proc ┌────────────┐
│ Telegram │ ─────────────▶│ bot svc │ ─────────────▶│ Postgres │
│ webhook │ │ + Fluent │ (PLpgSQL │ (scoring, │
└─────────────┘ │ i18n │ leaderboard)│ audit log)│
└──────┬───────┘ └─────┬──────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────────┐
│ moderation │ │ payout worker │
│ admin (manual │ │ (idempotent │
│ ~1M tasks/qtr) │ │ batched USDT │
└──────────────────┘ │ on Arbitrum) │
└──────────┬──────────┘
│ web3.py
▼
┌─────────────────────┐
│ smart contract │
│ 0xFd08…Cbb9 │
└─────────────────────┘ - idempotency: payout_id = hash(user_id, period, contract_addr) — UNIQUE-индекс в Postgres, повторный submit = no-op.
- audit_log пишется в той же транзакции, что и balance-update — ноль шансов на пропавшую выплату.
результаты
- $138k+ обработано платёжной системой за 7 месяцев на 63k+ MAU.
- 0 дублей выплат за всё время работы (1M+ операций модерации, тысячи batched-payouts).
- i18n покрытие через Fluent — 7.6% репозитория = .ftl-локали.
- до 25k просмотров на пост в продуктовом канале (telegram analytics).
- компактный stack: 3 сервиса в docker-compose, 1 host, ноль downtime по платежам.
что вынес
- Идемпотентность через UNIQUE-индекс в БД (а не in-memory dedup) — единственный способ выжить при перезапусках воркера выплат и retry-сценариях.
- PLpgSQL stored procedures для финансового скоринга оправданы, когда формула меняется ежемесячно — git-tracking миграции дают чёткую history без application redeploy.
- Fluent ставит DSL-планку для i18n в Telegram-продуктах с шестизначным денежным оборотом — pluralization и contextual variants нативно, минус целый класс bug-ов.