Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Architectural Masterclass: Building an Enterprise-Grade Core Ledger and Payment Gateway SaaS

2026年10月27日に「Dev.to」が公開したITニュース「Architectural Masterclass: Building an Enterprise-Grade Core Ledger and Payment Gateway SaaS」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

金融システム開発では、計算誤差は許されない。浮動小数点数は使わず、金額は最小単位の整数で管理する。複式簿記で取引の厳密な整合性を保証し、イベントソーシングや冪等性、回復力パターンを活用し、堅牢な基幹台帳・決済システムを構築する。

ITニュース解説

現代の金融技術、特に企業の核となる台帳システムや決済ゲートウェイの構築は、一般的なウェブアプリケーションの開発とは大きく異なる厳格な要件が伴う。通常のウェブアプリでは、一時的なデータの不整合やユーザーインターフェースの小さな表示崩れは許容される場合があるが、金融システムでは「誤差ゼロ」が絶対的な原則となる。お金を扱うシステムでは、たった一つの間違いも許されないからだ。そこでは、データの一貫性、トランザクションの安全性、そしてシステムの状態が常に予測可能であることが不可欠である。特に、二重仕訳の原則、ACID特性(原子性、一貫性、独立性、永続性)、そして厳密な数値計算が求められ、わずかな浮動小数点数の誤差がシステム全体の規制違反や壊滅的な損失を引き起こす可能性がある。

この金融システムの厳格なアーキテクチャを理解するために、ウェブ開発の進化を考えてみよう。初期のシングルページアプリケーション(SPA)では、ブラウザがデータの管理や画面の切り替えなど、ほとんどすべての処理を担っていた。しかし、ネットワークの不具合などでJavaScriptのファイルがうまく読み込めないと、画面が真っ白になり、システム全体が停止してしまうことがあった。これに対し、現在の堅牢なフルスタックアーキテクチャでは、「プログレッシブエンハンスメント」という考え方が重要視される。これは、まず基本的な機能が確実に動作する土台を築き、その上に非同期のJavaScriptなどの技術を段階的に追加してユーザー体験を向上させる、というアプローチである。このアーキテクチャの考え方を金融システムに適用すると、コア台帳と決済ゲートウェイが、決して停止せず、状態を失わず、データを破損させない「絶対的な構造的基盤」となる。他のマイクロサービスやリアルタイム分析ダッシュボード、非同期のWebhook処理などは、このコア台帳の上に積み重ねられる「段階的に強化されたレイヤー」と位置づけられる。つまり、もしこれらの追加レイヤーが一時的に機能しなくなったり、クラッシュしたりしても、基盤であるコア台帳は常に健全で、不変であり、数学的に正確な状態を保ち続ける必要があるのだ。

金融ソフトウェア開発における初心者の基本的な誤りの一つは、プログラミング言語に標準で備わる数値型、特にIEEE 754形式の浮動小数点数に依存することである。一般的なウェブ開発では、0.1と0.2を足すと0.30000000000000004のような結果になるのは「ちょっと変わった現象」として認識され、CSSのレイアウトが崩れたり、商品の推薦エンジンに影響を与えたりすることはめったにない。しかし、コア台帳のような金融システムでは、同じわずかな誤差が何百万もの利息計算、通貨換算、税金分割などで積み重なり、最終的に大きな財務のずれ、監査不能な記録、そして重大な規制不遵守を引き起こすことになる。この「IEEE 754の落とし穴」の原因は、コンピュータが数字を二進数(基数2)で保存するのに対し、人間が利用する金融システムは十進数(基数10)で運用されるためである。十進数で正確に表せる小数(例: 0.1)が、二進数では無限に続く小数になってしまう場合がある。コンピュータのメモリは有限であるため、IEEE 754標準ではこの無限に続く二進数を53ビットの精度で切り捨てなければならない。結果として保存される値は、真の十進数の近似値となる。複数の近似値が足し算、掛け算、割り算されると、丸め誤差が気づかないうちに蓄積されていく。この問題を金融システムで回避するには、二つの設計パターンを厳格に適用する必要がある。一つは「整数表現」で、すべての金額を最小通貨単位(例: 10.50ドルを1050セントのように整数で)として保存する方法だ。もう一つは「任意精度十進数ライブラリ」を利用する方法で、文字列ベースの十進数計算を行い、除算や乗算中に発生する切り捨てを防ぐための特別な数学的抽象化を用いる。整数表現であっても、除算の際に端数が発生することがあるため、堅牢なコア台帳アーキテクチャでは、確定的な丸めアルゴリズムと明示的な端数分配ルールを実装し、分割された部分の合計が元の金額と単一の最小単位まで正確に一致するように保証しなければならない。

会計は単にお金の出入りを記録するだけでなく、「資産 = 負債 + 資本」という基本的な会計方程式に支配される閉鎖的な数学システムである。トランザクション処理の文脈では、これは「すべてのデビットの合計はシステム全体のすべてのクレジットの合計と等しい」という不変の二重仕訳の原則に変換される。つまり、どんな金融取引も少なくとも二つのペアになったエントリ、すなわちデビットとクレジットから構成され、その合計が常に一致するという原則である。初心者が作るような単純な台帳では、開発者が「金額」と「タイプ」のカラムを持つテーブルを作成し、ユーザーアカウントの残高カラムを更新することがよくある。しかし、このアプローチは致命的な欠陥を抱えている。原子性(トランザクション全体が成功するか失敗するか)を侵害し、頻繁にアクセスされるアカウント行でデータベースのロック競合を引き起こし、過去の状態が上書きされてしまうため、履歴監査がほとんど不可能になる。本番環境で利用されるコア台帳は、「トランザクション(ジャーナル)」「仕訳(エントリ)」「勘定科目」という三つの明確な構造的要素に責任を分離する。トランザクションは、ビジネスイベントを表現する不変のラッパーであり、メタデータ、タイムスタンプ、関連IDなどを含む。仕訳は、トランザクションに属する個々の不可分な明細であり、勘定科目、方向(デビットまたはクレジット)、および金額を指定する。勘定科目は、仕訳が蓄積される会計科目のノードである。例えば、顧客がクレジットカードで100ドルをデジタルウォレットに入金する場合、システムは単に残高を更新するクエリを実行するのではなく、バランスの取れた仕訳を作成する。具体的には、Stripe清算勘定への100ドルのデビットと、顧客ウォレットへの100ドルのクレジットという二つの仕訳が記録される。もし、デビットとクレジットの合計が一致しないトランザクションがコミットされようとすると、データベーストランザクション全体が中断され、回復不可能な例外が発生する。

決済処理は本質的に非同期で分散しており、外部ネットワークの障害が発生しやすい。ユーザーが決済を開始すると、そのリクエストはクライアントのブラウザ、APIゲートウェイ、不正検出サービス、決済ネットワーク、取得銀行、発行銀行といった複数の外部システムを通過する。これらの各段階で遅延、タイムアウト、そしてあいまいな失敗状態が発生する可能性がある。この不確実性を克服するため、アーキテクチャではイベントソーシングと決定的有限状態機械を組み合わせたアプローチを採用する。イベントソーシングを用いた決済パイプラインでは、現在の状態は可変なテーブル行として保存されることはない。代わりに、状態は、不変なドメインイベントのシーケンスを順に「たたみ込む」ことで計算される。例えば、ChargeRequested(支払いの意図が受信される)、ChargeAuthorized(資金が承認される)、ChargeCaptured(資金の送金が明示的に要求される)、FundsSettled(物理的な通貨の移動が確認される)といったイベントが順に記録される。すべてのイベントは不変であり、タイムスタンプが付与されるため、監査人はトランザクションの正確なタイムラインをミリ秒単位で再構築できる。

分散システムではネットワークは信頼できない。クライアントアプリケーションが請求リクエストを送信し、HTTP 200 OK応答を受信する前に接続が切れてしまった場合、クライアントはゲートウェイが請求を処理したかどうかを知ることはできない。このような場合、クライアントアプリケーションの自然な動作はリクエストを再試行することだが、厳格な冪等性(Idempotency)制御がないと、ネットワークの再試行が二重請求につながり、金融システムにおいては壊滅的な失敗となる。この問題を防ぐため、決済ゲートウェイでは冪等性キーを強制する。クライアントは暗号学的に安全なランダムな識別子(UUIDv4など)を生成し、これをカスタムヘッダー(例: Idempotency-Key)として渡す。APIゲートウェイは、この冪等性キーがすでに存在するかどうかを、アトミックで高可用性のあるデータストアで確認する。新しいキーであれば処理を行いキャッシュするが、既に処理中のキーであれば競合を返すかポーリングを強制し、既に完了済みのキーであれば、トランザクションエンジンを再実行することなく、最初の実行時のキャッシュされたHTTP応答を即座に返す。これにより、同じリクエストを複数回実行しても、副作用の重複なく常に同じ結果が得られることが保証される。

決済ゲートウェイは、第三者の銀行システム、不正スコアリングAPI、カードネットワークなど、様々な外部サービスと連携する。これらの外部依存関係は、遅延の増加やサービス停止のリスクを抱えている。システムの障害を分離し、全体としての安定性を維持するために、エンタープライズレベルの決済パイプラインでは三つの主要な回復力パターンを実装する。一つ目は「サーキットブレーカー」で、これは外部サービスの失敗を監視し、失敗率が閾値を超えると「オープン状態」に切り替わり、劣化している依存関係にリクエストを送ることなく、迅速に失敗応答を返す。二つ目は「バルクヘッド」で、システムのリソースを隔離する。例えば、決済処理を分析レポートやWebhookの配信から厳密に分離することで、Webhookが停止しても決済受付スレッドは影響を受けずに動作を続けられる。三つ目は「デッドレターキュー(DLQ)」で、非同期イベントが再試行ポリシーを使い果たしても処理に失敗した場合、スタックトレースやエラーメタデータと共に自動的にDLQにルーティングされる。これにより、手動での検査や管理者が再処理を行うことが可能になる。

提供されているTypeScriptのコードは、このようなエンタープライズレベルのコア台帳システムを実装した例であり、最小通貨単位での厳密な整数演算、アトミックな二重仕訳検証、そして不変なイベントソースのトランザクション実行を実演している。コードでは、AccountType(資産、負債など)や PostingDirection(デビット、クレジット)といった基本的な会計概念が定義され、Account、Posting、``JournalTransactionといったデータ構造で会計要素をモデル化している。特にLedgerMathクラスのvalidateDoubleEntry メソッドは、すべての仕訳を検証し、デビットの合計がクレジットの合計と等しいかをチェックすることで、二重仕訳の不変条件を厳格に強制する。もし合計が一致しない場合は、即座にエラーを発生させ、トランザクションの永続化を防ぐ。CoreLedgerEngineクラスは、アカウントの作成、そしてcommitTransaction` メソッドによって、この二重仕訳の検証、参照整合性の確認、仮の残高計算、不変なトランザクションレコードの生成、そして最終的なアカウント残高への状態変更のアトミックな適用を行う。これにより、高信頼性の金融システムに求められる精密な数学的確実性、耐障害性、そして完璧な監査適合性を備えた堅牢な金融インフラを構築することが可能となる。

関連コンテンツ

関連IT用語

関連ITニュース