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

Финансовый ledger

Требования применяются к компоненту, предоставляющему возможность financial-ledger и являющемуся источником истины для изменения денежной позиции, остатка или обязательства. Уровни обязательности определены в корневом README.md.

В этом документе:

  • posting — неизменяемая запись об изменении денежной позиции, остатка или обязательства;
  • balance projection — вычисленный из postings остаток на определённой позиции ledger;
  • Command Query Responsibility Segregation (CQRS) — разделение моделей записи и чтения при общем или раздельных хранилищах;
  • Event Sourcing — модель, в которой упорядоченный поток domain events является источником истины, а текущее состояние восстанавливается их применением.

DATA-LED-001. Явная модель записи и источник истины

Уровень: MUST

Применяется к: каждому provider возможности financial-ledger

До реализации проект должен принять ADR по DEV-ADR-004, который определяет владельца, источник истины, ключ ledger, инварианты, транзакционную границу и одну модель записи:

  • append-only postings с прямым чтением;
  • append-only postings с CQRS в одном или разных хранилищах;
  • Event Sourcing, при котором поток событий является ledger.

CQRS и Event Sourcing не являются обязательными, если выбранные инварианты, нагрузка, аудит и восстановление обеспечиваются append-only postings без них. Balance projection, audit log и копия событий в message broker не должны неявно становиться источником истины.

Обоснование

Метка проекта «финтех» не определяет требования к согласованности, а неявный источник истины создаёт несколько конфликтующих денежных состояний.

Проверка

  • проверка принятого ADR и объявленной модели записи;
  • сопоставление write path, хранилищ и проекций с источником истины;
  • негативный тест изменения денежного состояния в обход ledger.

Исключения

Не допускаются для компонента, изменяющего денежную позицию, остаток или обязательство.

DATA-LED-002. Неизменяемость posting и исправление

Уровень: MUST

Применяется к: принятому ledger posting

Приложение не должно изменять или удалять принятый posting. Исправление, сторнирование или отмена должны создавать новый posting со стабильной ссылкой на исправляемую запись и отдельной причиной. Исходный и исправляющий postings должны оставаться различимыми при чтении, сверке и аудите.

Обоснование

Изменение истории уничтожает возможность доказать происхождение остатка и отличить исправление от первоначальной операции.

Проверка

  • проверка запрета update и delete для application identity;
  • тест исправления и повторного исправления;
  • сверка исходного и исправляющего postings с balance projection.

Исключения

Контролируемое удаление после утверждённого срока хранения MAY выполняться отдельным административным процессом, если оно не меняет активный остаток, соблюдает legal hold и оставляет audit удаления.

DATA-LED-003. Атомарность и идемпотентность операции

Уровень: MUST

Применяется к: команде, создающей один или несколько ledger postings

Команда должна иметь стабильный идентификатор операции или idempotency key. Повтор с тем же ключом и функционально эквивалентным входом должен возвращать тот же результат без новых postings; тот же ключ с отличающимся входом должен возвращать определённый конфликт.

Все postings и результат одной локальной ledger-операции должны фиксироваться атомарно. Конкурентная запись должна проверять ожидаемую версию либо использовать эквивалентный механизм по BE-STATE-003. Если обязательный эффект фиксируется в другой системе, операция должна соблюдать BE-STATE-007 и применимые INT-WF-001INT-WF-007.

Обоснование

Timeout, retry и конкурентное выполнение не должны удваивать денежный эффект или оставлять частично зафиксированную операцию.

Проверка

  • повтор команды до и после commit;
  • тот же idempotency key с отличающимся входом;
  • fault-injection между postings одной операции;
  • конкурентный тест одной ledger-позиции.

Исключения

Не допускаются для операции, изменяющей денежную позицию, остаток или обязательство.

DATA-LED-004. Точное денежное представление

Уровень: MUST

Применяется к: сумме, валюте и вычислению ledger

Контракт должен задавать идентификатор валюты или единицы, допустимую точность, масштаб и правило округления для каждой операции преобразования. Денежная сумма в источнике истины не должна храниться или вычисляться типом с двоичной плавающей точкой.

Для группы postings должен быть определён проверяемый инвариант сохранения денежного значения по каждой валюте или единице. Операция, нарушающая инвариант, превышающая допустимый диапазон либо смешивающая валюты без явно зафиксированного преобразования, должна отклоняться до commit. Отсутствующая сумма должна отличаться от нулевой.

Обоснование

Неявные масштаб, округление и преобразование валют создают накопительное расхождение, которое выглядит как допустимое числовое значение.

Проверка

  • boundary- и property-тесты точности, диапазона и округления;
  • тест инварианта для каждой поддерживаемой валюты или единицы;
  • негативный тест binary floating point в persisted schema;
  • тест отсутствующей и нулевой суммы.

Исключения

Приближённое значение MAY использоваться в ненормативной аналитике, но не должно записываться обратно в ledger или использоваться для денежного решения.

DATA-LED-005. Balance projection и сверка

Уровень: MUST

Применяется к: сохранённому остатку или query model финансового ledger

Balance projection должна содержать позицию или версию источника, быть воспроизводимой из ledger и не должна принимать запись в обход postings. Проект должен определить максимальную допустимую свежесть, численный максимальный интервал сверки, допустимое расхождение и владельца реакции.

Сверка должна обнаруживать пропущенные и лишние postings, дубликаты, расхождение позиции и контрольных сумм. Превышение допустимого расхождения должно создавать alert и блокировать использование неподтверждённого остатка для нового необратимого финансового решения.

Межсервисная query model должна дополнительно соблюдать ARC-SVC-009 и ARC-SVC-010, а при обновлении событиями — INT-EVT-013 и применимое INT-EVT-014.

Обоснование

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

Проверка

  • полное пересоздание balance projection из ledger;
  • контролируемый пропуск, дубликат и перестановка postings;
  • тест freshness и превышения допустимого расхождения;
  • негативный тест записи остатка в обход ledger.

Исключения

Остаток, вычисляемый непосредственно из полного ledger при каждом запросе, MAY не иметь отдельного интервала сверки, если эквивалентность результата проверяется запросом и тестом инвариантов.

DATA-LED-006. Разделение ledger и audit

Уровень: MUST

Применяется к: финансовой операции и её ledger postings

Ledger и audit log должны оставаться разными источниками с разными схемами, правами доступа и сроками хранения. Audit должен фиксировать субъекта, команду и результат по OBS-AUD-001OBS-AUD-009, но не должен использоваться для восстановления денежного остатка.

Стабильный идентификатор операции должен храниться в posting. Поле correlation_id audit и применимого интеграционного события должно содержать этот идентификатор. Полные платёжные реквизиты, секреты и персональные данные не должны дублироваться в postings или events без разрешённой цели и правил обработки по DATA-CLS-001DATA-CLS-003.

Обоснование

Audit доказывает действие субъекта, а ledger — денежный результат; смешение назначений нарушает полноту обоих и увеличивает объём защищаемых данных.

Проверка

  • сквозной тест корреляции команды, postings и audit;
  • сверка прав доступа и retention ledger и audit;
  • schema review и сканирование запрещённых данных;
  • тест, что потеря audit-проекции не изменяет balance.

Исключения

Не допускаются для разделения источника денежного состояния и audit log.

DATA-LED-007. Авторитетный event store

Уровень: MUST

Применяется к: финансовому ledger, выбравшему Event Sourcing

Event store должен быть единственным источником истины для event-sourced состояния. Для каждого stream должны быть определены стабильный ключ, монотонная позиция, закрытая транзакционная граница и атомарное сравнение ожидаемой позиции при append. Конфликт позиции должен отклонять весь append без частичной записи.

Message broker или его retention не должны считаться event store, если контракт не обеспечивает чтение полного stream по ключу, атомарный append с проверкой ожидаемой позиции, backup и восстановление в пределах заданных Recovery Point Objective (RPO) и Recovery Time Objective (RTO).

Обоснование

Гарантия доставки сообщений не обеспечивает конкурентную запись и полное восстановление состояния одного ledger stream.

Проверка

  • конкурентный append с одной ожидаемой позицией;
  • чтение полного stream по ключу и проверка отсутствия gap;
  • восстановление event store из backup;
  • проверка заявленных гарантий отдельно от broker acknowledgement.

Исключения

Не допускаются для event-sourced источника истины.

DATA-LED-008. Детерминированный replay и эволюция событий

Уровень: MUST

Применяется к: event-sourced финансовому ledger

Domain event должен иметь версионируемую схему и неизменяемую позицию в stream. Одинаковые версии кода, конфигурации и упорядоченного stream должны восстанавливать функционально эквивалентное состояние. Изменение схемы должно определять чтение каждой сохраняемой версии посредством нового event, версионированного преобразования или другого воспроизводимого механизма; перезапись опубликованной истории запрещена.

Snapshot MAY ускорять восстановление, но не должен становиться источником истины. Он должен содержать позицию stream, проверяться replay последующих events и удаляться с полным пересозданием без изменения результата.

Обоснование

Event Sourcing без проверяемого replay и эволюции схемы сохраняет историю, которую новая версия приложения не может безопасно прочитать или восстановить.

Проверка

  • given-when-then тест исторических events и создаваемых events;
  • полный replay каждой поддерживаемой версии схемы;
  • replay от snapshot и от начала stream с эквивалентным результатом;
  • негативный тест изменённого или пропущенного event.

Исключения

Не допускаются для опубликованной истории event-sourced ledger.