ИНЖЕНЕРИЯ
Почему каждый баланс должен выводиться из леджера с двойной записью
27 июля 2026 г. · 9 мин чтения · Coreza
В каждой банковской системе есть таблица, отвечающая на самый важный вопрос продукта: сколько денег у этого клиента? Проектное решение, отделяющее надёжные коры от хрупких, — хранится ли это число (колонка, которую кто-то обновляет) или выводится (сумма записей append-only леджера). Звучит как деталь реализации. На деле это разница между системой, которая может доказать свои балансы, и системой, которая может их только утверждать.
Это самое значимое архитектурное решение в финтех-коре — и оно почти невидимо на демо. Два продукта могут выглядеть на экране одинаково, при этом один обновляет колонку баланса на месте, а другой выводит каждый баланс из леджера с двойной записью. Разница проявляется позже — в расхождениях сверки, в тикетах поддержки, которые никто не может объяснить, и в аудитах.
Что означает двойная запись в инженерном контексте
Двойной записи пять веков, но в банковском бэкенде это не бухгалтерская формальность — это инвариант, который обеспечивает база данных. Каждое движение денег — транзакция минимум с двумя проводками: один счёт дебетуется, другой кредитуется, и сумма проводок равна нулю. Деньги никогда не создаются и не уничтожаются оператором UPDATE; они только перемещаются между счетами, и само перемещение и есть запись.
Баланс клиента тогда — это проекция: сумма всех проводок леджера, затрагивающих этот счёт. Кэшируемая ради производительности — да, но всегда пересчитываемая, и именно пересчёт является истиной. Если кэш и леджер когда-либо расходятся, побеждает леджер, а само расхождение — сигнал, что что-то нужно расследовать.
Инвариант нулевой суммы даёт свойство, которого не может предложить ни один дизайн с хранимыми балансами: всю систему можно проверить. Просуммируйте каждую проводку по каждому счёту — кошельки клиентов, комиссионный доход, расчётные счета провайдеров, транзитные счета — и итог равен нулю. Любой баг, гонка или частичный сбой, теряющий деньги, ломает это уравнение и становится обнаружимым, обычно в тот же день.
Как расходятся хранимые балансы
Режим отказа дизайна с колонкой баланса не драматичен. Это медленный дрейф, порождаемый обычным продакшен-трафиком:
- Конкурентность: два вывода средств читают один и тот же баланс в один момент, оба проходят проверку, оба записывают. Без леджера не остаётся записи о том, каким баланс должен был быть.
- Повторы и частичные сбои: платёжный провайдер отвечает таймаутом, клиент повторяет запрос, и зачисление приходит дважды — или сбой между «обновить баланс» и «записать транзакцию» оставляет их навсегда несогласованными.
- Ручные операции: инженер поддержки закрывает жалобу прямым UPDATE. Клиент доволен; у появившихся денег нет происхождения, и ни один отчёт никогда их не объяснит.
- Комиссии и округления: комиссия, рассчитанная в одном месте и применённая в другом, конвертация валюты, округлённая по-разному с каждой стороны, — доли цента, накапливающиеся в реальные расхождения.
Правила, которые навязывает леджер, — и все они хорошие
Ставка на выводимые балансы дисциплинирует весь остальной кор. Эти ограничения кажутся строгими в первый день и оказываются причиной, по которой система остаётся корректной на третий год:
- Целочисленные суммы в минимальных единицах — центы, сатоши, wei — и никогда числа с плавающей запятой. Двоичные float не могут точно представить 0,10; леджер, обязанный суммироваться в ноль, нельзя строить на числах, которые сходятся почти.
- Неизменяемые проводки: проведённая запись никогда не редактируется и не удаляется. Ошибки исправляются так, как банки исправляли их всегда, — сторнирующей проводкой, которая оставляет в записи и ошибку, и исправление.
- Идемпотентность: каждая операция несёт ключ, поэтому повторённый запрос проводится один раз. Леджер делает дубликаты видимыми; идемпотентность их предотвращает.
- Атомарность: дебет, кредит и изменение бизнес-состояния коммитятся вместе или не коммитятся вовсе. Ни один путь в коде не обновляет баланс вне транзакции леджера — включая административные корректировки, которые становятся обычными атрибутируемыми проводками вместо тихих перезаписей.
Сверка: доказательство перед внешним миром
Внутренний инвариант доказывает, что система согласована сама с собой. Сверка доказывает, что она согласована с реальностью. Финтех-кор держит позиции у внешних сторон — банковские рельсы, карточные процессоры, блокчейн-сети — и каждая из них хранит собственную версию истины. Ежедневная сверка сравнивает каждую выписку провайдера и каждую ончейн-позицию с соответствующими счетами леджера и заставляет объяснить каждую разницу: расчёт в пути, ещё не проведённая комиссия провайдера или настоящее расхождение, которое в тот же день получает транзитную проводку и ответственного.
Сверка — то место, где архитектура окупается. С леджером с двойной записью расхождение — это ограниченный поиск: день появления, затронутые счета, вовлечённые проводки. С хранимыми балансами то же расследование начинается с числа без истории — поэтому несведённые разницы в системах с колонкой баланса чаще списывают, чем разрешают.
Криптоактивы поднимают ставки. Ончейн-баланс публичен: кто угодно может сравнить кошельки, которые вы контролируете, с балансами, которые вы показываете клиентам. Некастодиальное хранение без леджера, ежедневно сверяемого с сетью, — это расхождение, ждущее, пока его обнаружит кто-то вне компании.
Что на самом деле спрашивают аудиторы и регуляторы
Рано или поздно регулируемый финтех сталкивается с какой-то версией одного и того же вопроса: докажите, что балансы клиентов верны. Практический ответ — цепочка доказательств: каждый баланс выводится из append-only леджера; леджер суммируется в ноль; он ежедневно сверяется с каждой внешней позицией; а неизменяемый журнал аудита с хеш-цепочкой фиксирует, кто инициировал каждое движение, с какого устройства и под каким согласованием.
Команды, строившие на хранимых балансах, отвечают на эти вопросы криминалистикой — восстанавливая историю из логов приложения и выгрузок провайдеров, в худший из возможных моментов и в условиях дедлайна. Команды с леджером отвечают запросом. Аудит не создаёт требование; он лишь показывает, выполняла ли его архитектура с самого начала.
Тест, который стоит устроить любой платформе кор-банкинга
Если вы оцениваете платформу — или пересматриваете ту, что построили сами, — эти вопросы задаются за пять минут, а ответы трудно подделать:
- Откуда берётся число на экране клиента? Если ответ — колонка баланса, а не проекция леджера, всё остальное — декорация.
- Можно ли изменить баланс, не создав проводок в леджере? Спросите отдельно про административные инструменты и процессы поддержки — именно там прячутся тихие UPDATE.
- Являются ли суммы целыми числами в минимальных единицах от края до края, включая криптосторону?
- Покажите вчерашнюю сверку: каждый провайдер, каждая сеть, каждая разница объяснена. Платформа, которая не может это показать, этого не делает.
- Как исправляется ошибочная проводка? Единственный хороший ответ — сторнирующая проводка, атрибутированная человеку, за процессом согласования.
Где стоит Coreza
Coreza построена на строгой версии этого дизайна: каждый баланс — фиатный и крипто — выводится из леджера с двойной записью; суммы везде целые в минимальных единицах; проведённые записи неизменяемы, а исправления делаются сторнирующими проводками за согласованием maker-checker; и каждый инстанс ежедневно сверяется с каждым подключённым провайдером и каждой сетью, в которой работает. Журнал аудита append-only и связан хеш-цепочкой: он фиксирует кто, когда и откуда для каждого движения.
Ничего из этого не видно на демо продукта — и в этом весь смысл статьи: леджер — та часть платформы кор-банкинга, которую нельзя оценить, глядя на экраны. Версия этого кора обрабатывает реальных клиентов и реальные деньги в регулируемом европейском финтехе с 2024 года — а пять вопросов выше мы предлагаем задать любой платформе, включая нашу.
Готовы увидеть свой банк в действии?
Расскажите нам о своём проекте — и мы вернёмся к вам с живой демонстрацией платформы и индивидуальным предложением для вашего рынка.
Назначить встречу