Skip to content
Published on

銀行預金システムアーキテクチャ — 勘定系の心臓を解剖する

Authors

はじめに

銀行ITにおいて「勘定系」という言葉の重みは別格です。チャネルが止まれば顧客は不便を感じますが、勘定系が止まれば銀行そのものが止まります。その勘定系の中心にあるのが預金(受信)システムです。顧客がATMで出金ボタンを押した瞬間から、元帳の残高が更新され取引明細が確定するまで、そのわずか数百ミリ秒の中に、数十年かけて磨かれてきた設計原則が凝縮されています。

本記事は、コアバンキングの預金ドメインに初めて触れる開発者、そして次世代システムプロジェクトやチャネル連携開発で勘定系と向き合うエンジニアのために書きました。預金業務ドメインの構造、元帳テーブル設計、取引処理シーケンス、同時実行制御、日次締め処理と無停止運用の共存問題まで、実務の視点で解剖していきます。なお本記事は一般的な技術解説であり、特定金融機関の内部実装の説明でも、投資・法律上の助言でもないことをあらかじめお断りしておきます。

預金業務ドメインの概観

預金業務とは何か

銀行業務は大きく預金(お金を預かる業務)と融資(お金を貸す業務)に分かれます。預金業務は顧客から預金を受け入れて管理する業務全般を指し、銀行から見れば負債(顧客に返すべきお金)です。代表的な預金商品は次のとおりです。

商品タイプ特徴利息構造設計上のポイント
普通預金(流動性)入出金自由低い変動金利、決算期に支払高頻度取引、同時実行制御
定期預金(据置型)まとまった資金を一定期間預入約定金利、満期一括支払満期管理、中途解約利率
定期積金・積立(積立型)毎月一定額を払込回次ごとの日割り計算回次管理、未納・先納処理
自由積立自由なタイミングで払込払込日ごとの日割り計算明細単位の利息計算
MMDA市場金利連動の流動性預金残高帯別の段階金利日次残高の追跡

同じ「預金」でも取引パターンはまったく異なります。普通預金は毎秒数千件の入出金が集中する高頻度トランザクションのドメインであり、定期預金は取引頻度こそ低いものの、利息計算と満期処理の整合性が生命線です。システム設計もこの違いを反映しなければなりません。

口座のライフサイクル

預金口座は明確な状態機械(ステートマシン)を持ちます。

                  +----------+
   新規(開設)      |          |  取引停止事由の発生
  ------------->  |  正常     | ----------------------+
                  | (ACTIVE) |                        |
                  +----------+                        v
                    |      ^                    +-----------+
            解約申込 |      | 停止解除            |  取引停止   |
                    |      +------------------- | (BLOCKED) |
                    v                           +-----------+
              +----------+                            |
              |  解約     |  <-------------------------+
              | (CLOSED) |        強制解約(法的事由など)
              +----------+
                    |
                    v  一定期間経過
              +----------+        +-----------+
              | 休眠移行  | -----> | 拠出/消滅   |
              | (DORMANT)|        | (ESCHEAT) |
              +----------+        +-----------+

実務で重要なのは状態遷移の副作用です。たとえば解約時点では未払利息を精算して支払う必要があり、休眠移行時点では休眠預金管理の仕組み(公的機関への拠出など)へ引き渡す別プロセスが起動します。状態遷移のたびに会計仕訳が付随する点こそ、一般的なCRUDアプリケーションとコアバンキングの最大の違いです。

勘定系・情報系・対外系 — 銀行システムの3階層

韓国の銀行ITは伝統的に、業務の性質に応じてシステムを区分してきました。

 +-----------------------------------------------------------------+
 |                       チャネル系 (Channel)                        |
 |  ネットバンキング  モバイル  ATM  窓口端末  テレホン  オープンAPI     |
 +------------------------------+----------------------------------+
                                |  電文 / API
 +------------------------------v----------------------------------+
 |                       勘定系 (Core)                               |
 |  +-----------+  +-----------+  +-----------+  +--------------+   |
 |  |   預金     |  |   融資     |  |   外為     |  |  共通(顧客、   |   |
 |  | (預金元帳)  |  | (貸出元帳)  |  | (外貨元帳)  |  |  商品、会計)   |   |
 |  +-----------+  +-----------+  +-----------+  +--------------+   |
 +-------+----------------------------------------------+----------+
         | 取引ログ/元帳複製 (CDC、バッチ)                  | 対外電文
 +-------v------------------+  +------------------------v----------+
 |       情報系 (DW/Mart)     |  |          対外系 (External G/W)     |
 |  経営情報  リスク  マーケ    |  |  金融決済院  中央銀行  カード網      |
 |  顧客分析  規制報告(BIS等)  |  |  共同網  CMS  オープンバンキング     |
 +---------------------------+  +-----------------------------------+
  • 勘定系は元帳(レジャー)を保有する記録システム(System of Record)です。残高の真実は勘定系にのみ存在します。
  • 情報系は勘定系データを複製・加工した分析システムです。経営レポート、リスク計測、マーケティング施策がここで動き、照会系の負荷を吸収して勘定系のオンライン性能を守ります。
  • 対外系は金融決済院(KFTC)の共同網、中央銀行網、SWIFTなど外部機関との電文送受信を担うゲートウェイです。他行振込が可能なのは対外系があるからです。

この区分が重要なのは、データ一貫性の等級が異なるからです。勘定系には強い一貫性(トランザクションACID)が必須であり、情報系は数分から1日程度の遅延が許容されます。チャネルで「昨日時点の平均残高」を表示するときに勘定系を直接叩いてはいけない理由でもあります。

預金元帳の設計 — テーブルで見るコア

預金元帳の骨格は3種類のテーブルです。口座マスタ、取引明細、そして残高(または残高履歴)です。以下は概念説明のために単純化したスキーマ例です(実際の次世代システムのカラム数は数百に及びます)。

口座マスタ

CREATE TABLE deposit_account_master (
    account_no        CHAR(14)      NOT NULL,  -- 口座番号
    customer_id       VARCHAR(20)   NOT NULL,  -- 顧客番号
    product_code      VARCHAR(10)   NOT NULL,  -- 商品コード
    currency_code     CHAR(3)       NOT NULL DEFAULT 'KRW',
    account_status    CHAR(2)       NOT NULL,  -- 01:正常 02:停止 03:解約 04:休眠
    open_date         DATE          NOT NULL,  -- 新規日
    maturity_date     DATE,                    -- 満期日(据置/積立型)
    contract_rate     NUMERIC(7,4),            -- 約定金利(年利、%)
    contract_amount   NUMERIC(17,0),           -- 契約金額(積金の月掛金など)
    last_txn_date     DATE,                    -- 最終取引日(休眠判定用)
    branch_code       VARCHAR(6)    NOT NULL,  -- 管理店
    passbook_seq      INTEGER       DEFAULT 0, -- 通帳繰越回数
    created_at        TIMESTAMP     NOT NULL,
    updated_at        TIMESTAMP     NOT NULL,
    CONSTRAINT pk_dam PRIMARY KEY (account_no)
);

CREATE INDEX ix_dam_customer ON deposit_account_master (customer_id);
CREATE INDEX ix_dam_maturity ON deposit_account_master (maturity_date, account_status);

満期日のインデックスは、満期到来口座を抽出する日次バッチの生命線です。休眠判定のための最終取引日の管理もマスタの役割です。

取引明細(ジャーナル)

CREATE TABLE deposit_transaction_history (
    account_no        CHAR(14)      NOT NULL,
    txn_date          DATE          NOT NULL,  -- 取引日(営業日基準)
    txn_seq           INTEGER       NOT NULL,  -- 口座別取引連番
    txn_datetime      TIMESTAMP     NOT NULL,  -- システム時刻
    txn_type          CHAR(2)       NOT NULL,  -- 01:入金 02:出金 03:利息 04:訂正
    txn_amount        NUMERIC(17,0) NOT NULL,  -- 取引金額
    balance_after     NUMERIC(17,0) NOT NULL,  -- 取引後残高(通帳印字用)
    counterparty      VARCHAR(60),             -- 摘要/相手口座の表示
    channel_code      CHAR(2)       NOT NULL,  -- チャネル区分(窓口/ATM/モバイル)
    teller_id         VARCHAR(10),             -- 取扱者(窓口取引)
    global_txn_id     VARCHAR(32)   NOT NULL,  -- 取引固有番号(全体トレース用)
    reversal_yn       CHAR(1)       NOT NULL DEFAULT 'N',  -- 取消取引フラグ
    original_txn_ref  VARCHAR(32),             -- 原取引参照(取消時)
    CONSTRAINT pk_dth PRIMARY KEY (account_no, txn_date, txn_seq)
);

CREATE UNIQUE INDEX ux_dth_global ON deposit_transaction_history (global_txn_id);

設計上のポイントが3つあります。

  1. 取引明細は不変(append-only)です。誤った取引はUPDATEで直すのではなく、逆方向の取消(訂正)取引を追加します。元帳の監査証跡性はここから生まれます。
  2. 取引後残高をジャーナル自体に記録します。通帳印字や紛争対応のためでもありますが、残高検証バッチが「直前の取引後残高 + 取引金額 = 当該取引後残高」のチェーンを検査するためにも使われます。
  3. 取引固有番号(グローバルトランザクションID)はチャネルから対外系まで貫通するトレースキーです。タイムアウト後の結果照会(原取引確認)と二重防止(冪等性)の基準になります。

残高管理

CREATE TABLE deposit_balance (
    account_no        CHAR(14)      NOT NULL,
    current_balance   NUMERIC(17,0) NOT NULL,  -- 現在残高
    available_balance NUMERIC(17,0) NOT NULL,  -- 出金可能残高
    hold_amount       NUMERIC(17,0) NOT NULL DEFAULT 0,  -- 支払停止/差押金額
    uncleared_amount  NUMERIC(17,0) NOT NULL DEFAULT 0,  -- 未決済他店券(手形/小切手)
    last_txn_seq      INTEGER       NOT NULL,  -- 最終取引連番
    version_no        BIGINT        NOT NULL DEFAULT 0,  -- 楽観ロック用バージョン
    updated_at        TIMESTAMP     NOT NULL,
    CONSTRAINT pk_db PRIMARY KEY (account_no)
);

現在残高と出金可能残高が異なるという点が、預金ドメインの繊細なところです。差押え・質権設定された金額や、まだ決済が完了していない他店券の入金額は、残高にはあっても出金できません。出金可否の判定式はおおよそ次のとおりです。

出金可能残高 = 現在残高 - 支払停止金額 - 未決済他店券 + 当座貸越限度(約定時)
出金承認条件: 出金依頼金額 <= 出金可能残高

取引処理の流れ — ある出金の一生

入出金シーケンス

モバイルアプリで出金(振込出金)が発生するときの流れを単純化すると次のとおりです。

 顧客アプリ   チャネルサーバ    勘定系(預金)             DB
   |            |               |                    |
   |--出金依頼-->|               |                    |
   |            |--電文(出金)--->|                    |
   |            |               |--1. 口座状態の検証-->|  (正常? 停止?)
   |            |               |--2. 残高行ロック---->|  SELECT ... FOR UPDATE
   |            |               |--3. 出金可能額検証-->|
   |            |               |--4. 残高の減算------>|  UPDATE balance
   |            |               |--5. 取引明細INSERT-->|
   |            |               |--6. 会計仕訳の記録-->|
   |            |               |<------ COMMIT ------|
   |            |<--応答(承認)---|                    |
   |<--完了表示--|               |                    |

肝心なのは、ステップ2から5までが1つのDBトランザクションだという点です。残高検証と減算の間に別の取引が割り込めば二重出金が発生するため、検証と更新は必ず同じロック区間の中で行わなければなりません。

悲観ロック — コアバンキングの基本動作

伝統的なコアバンキングは悲観ロックを使います。残高行を先にロックしてから検証・更新する方式です。

-- トランザクション開始
BEGIN;

-- 1) 残高行を排他ロックで読む
SELECT current_balance, available_balance, last_txn_seq
  FROM deposit_balance
 WHERE account_no = '11012345678901'
   FOR UPDATE;

-- 2) アプリケーション側で出金可能額を検証した後、減算する
UPDATE deposit_balance
   SET current_balance   = current_balance - 500000,
       available_balance = available_balance - 500000,
       last_txn_seq      = last_txn_seq + 1,
       updated_at        = CURRENT_TIMESTAMP
 WHERE account_no = '11012345678901';

-- 3) 取引明細を記録する
INSERT INTO deposit_transaction_history
       (account_no, txn_date, txn_seq, txn_datetime, txn_type,
        txn_amount, balance_after, channel_code, global_txn_id, reversal_yn)
VALUES ('11012345678901', CURRENT_DATE, 124, CURRENT_TIMESTAMP, '02',
        500000, 1500000, '03', 'G20260613000000123456', 'N');

COMMIT;

悲観ロックは整合性が確実な代わりに、ホットアカウント(法人のマスター口座、バーチャル口座の親口座のように取引が集中する口座)でロック待ちが長くなります。給与振込日に特定法人口座へ数万件が集中すると、その口座の残高行が直列化ポイントになります。

楽観ロックと折衷案

照会が圧倒的に多く競合がまれな領域では、バージョンカラムによる楽観ロックも使われます。

UPDATE deposit_balance
   SET current_balance = current_balance - 500000,
       version_no      = version_no + 1
 WHERE account_no = '11012345678901'
   AND version_no  = 42;   -- 読んだときのバージョンと一致する場合のみ成功
-- 更新件数が0なら競合 → リトライ

実務上の折衷案は次のように整理できます。

戦略適する場面リスク
悲観ロック(FOR UPDATE)一般の入出金、出金系全般ホットアカウントのロック競合
楽観ロック(バージョン)競合がまれな更新、限度変更などリトライ多発時のスループット急落
条件付きUPDATEによる減算単純減算(可用額検証込み)複雑な検証ロジックの表現に限界
口座単位の直列化キュー超高頻度のマスター口座インフラ複雑度の増加

条件付きUPDATEは検証をSQLの中へ押し込むテクニックです。

UPDATE deposit_balance
   SET available_balance = available_balance - 500000,
       current_balance   = current_balance - 500000
 WHERE account_no = '11012345678901'
   AND available_balance >= 500000;  -- 更新0件なら残高不足として拒否

振込と他行取引 — 金融決済院の共同網

自行振込と他行振込

同一銀行内の振込(自行振込)は、出金口座と入金口座を1つのトランザクションにまとめられます(この場合もデッドロック防止のため、口座番号のソート順にロックするのが定石です)。問題は他行振込です。相手銀行の元帳には手を出せないため、金融決済院(KFTC)が運営する電子金融共同網を通じて電文をやり取りします。

 A銀行(出金側)            金融決済院(中継)            B銀行(入金側)
     |                        |                        |
     |--1. 出金(自行元帳)      |                        |
     |--2. 他行入金依頼電文---->|                        |
     |                        |--3. 入金依頼電文-------->|
     |                        |                        |--4. 入金(B行元帳)
     |                        |<--5. 入金完了応答--------|
     |<--6. 完了応答-----------|                        |
     |--7. 取引確定            |                        |
     |                        |                        |
     |     (銀行間の実際の資金は中央銀行の決済網による       |
     |      時点ネット決済で別タイミングで清算される)         |

ここで重要な概念の分離が1つあります。顧客元帳への反映(即時)と銀行間の資金決済(時点ネット決済)はタイミングが異なるという点です。顧客は即時に入金を確認できますが、銀行間の清算は中央銀行決済網(BOK-Wire+)を通じた差額決済で処理されます。そのため銀行は相手銀行の決済不履行リスクを管理する必要があり、それが担保・限度管理といった規制上の仕組みにつながります。

タイムアウトと補償取引

他行取引の悪夢は「不明」状態です。応答5が返ってこなければ、入金されたのかどうかわかりません。このときの対応パターンは次のとおりです。

  1. 原取引確認(結果照会)電文で相手システムに結果を問い合わせます。取引固有番号がトレースキーです。
  2. それでも不明なら保留処理し、自動/手動の照合(リコンサイル)プロセスに引き渡します。出金は完了したのに入金が不明な場合、顧客資産が宙に浮いた状態なので最優先です。
  3. 最終的に失敗と確定したら、補償取引(出金取消)を自動生成します。原取引を消すのではなく、反対取引を追加するのが原則です。

分散トランザクション(2PC)を銀行間に張ることはできないため、コアバンキングの他行取引は本質的に補償トランザクション(Saga)パターンの元祖と言えます。

オンラインとバッチの二元構造

預金システムは2つの顔を持ちます。昼(と夜)のオンライン取引処理、そして日単位のバッチ処理です。

区分オンラインバッチ
単位取引1件全口座または抽出対象
時間制約数十から数百msバッチウィンドウ(数十分から数時間)
代表業務入出金、振込、照会利息計算、満期処理、休眠移行、残高検証
失敗時の処理即時拒否/補償リスタート、区間再処理

代表的な日次バッチ(EOD、End of Day)の流れは次のとおりです。

 [営業日Dの締め開始]
    1. オンラインのカットオーバー準備 (新規取引の計上日をD+1へ切替)
    2. 未決取引の整理 (対外取引の照合、保留取引の確定)
    3. 利息計算バッチ (日積数の積上げ、決算対象利息の確定)
    4. 満期到来処理 (満期案内、自動継続、自動解約)
    5. 休眠/長期未取引の判定
    6. 元帳と会計(総勘定元帳)の照合
    7. 情報系へのデータ連携 (CDC補正、日次スナップショット)
    8. 日計表/現金在高の締め確定
 [営業日D+1の開始]

24時間365日無停止と日次締めの共存

かつては日次締めの間オンラインを停止していましたが、いまや午前3時でも振込ができる時代です。無停止と日次締めを共存させる代表的な手法は次のとおりです。

  1. 計上日の切替: システムは止めず、深夜0時または締め時点以降の取引にD+1の計上日を付与します。「今日」という概念を取引時刻ではなく計上日に分離するのです。
  2. 先日付バッファ領域: 締めバッチが走っている間に入ってきた取引を別バッファ(またはD+1パーティション)に記録し、締め完了後に本元帳のフローへ合流させます。
  3. 締めバッチのオンライン親和化: 全口座をロックする代わりにパーティション単位に分割して処理し、処理中のパーティション外の口座のオンライン取引はそのまま流します。
  4. 無停止の元帳切替: 残高スナップショットをD時点で論理的に凍結し、以降の取引はD+1残高にのみ反映します。日計表などの締め成果物はスナップショット基準で作成します。

ここでよく発生する実務上の落とし穴が「締め処理中の取引の二重反映」です。利息計算バッチが残高を読む時点と、オンライン取引が残高を変更する時点が重なると、日積数(日次残高の累計)が揺らぎます。そのため日積数は現在残高を読むのではなく取引明細から再構成可能でなければならず、締め成果物は必ず「どの時点基準か」が明示されたスナップショットから作る必要があります。

性能要件 — 数字で見る勘定系

大手商業銀行の規模でよく言及される水準は次のとおりです(機関・時期により差が大きいため、あくまで桁感覚としてください)。

項目一般的な目標水準
平常時の勘定系取引量毎秒数百から数千件
ピーク(給与日、連休前)平常時の3倍から10倍
オンライン応答時間勘定系内部処理100ms以内、単純照会はさらに短く
可用性目標年99.9パーセント以上(計画停止を除く)、重要区間はさらに高く
日次バッチウィンドウ数時間以内に完了、遅延すると営業開始リスク

性能設計におけるコアバンキング特有のポイントは次のとおりです。

  • 読み取り負荷の分離: 残高照会、取引明細照会はリードレプリカまたは情報系へ逃がし、元帳DBには更新トランザクションだけを残します。ただし出金直前の残高検証は必ずプライマリで行います。
  • ホットアカウント対策: バーチャル口座の親口座のように更新が集中する口座は、残高のシャーディング(サブ残高分割)や、入金系を非同期集計で流す設計を検討します。
  • 取引明細のパーティショニング: 取引日基準のパーティショニングでインデックスの深さを制御し、過去明細はアーカイブ層へ移管します。

障害シナリオと対応

冗長化構成

        [チャネル]                 [チャネル]
           \                         /
        +---v-------------------------v---+
        |       L4/電文ルーティング         |
        +---+-------------------------+---+
            |        (Active)         |        (Active or Standby)
     +------v------+           +------v------+
     |  APサーバ#1  |           |  APサーバ#2  |    <- ステートレス、水平拡張
     +------+------+           +------+------+
            |                         |
        +---v-------------------------v---+
        |       元帳DB (Primary)           |
        +---------------+------------------+
                        | 同期レプリケーション
        +---------------v------------------+
        |       元帳DB (Standby)            |  <- 同一センターまたは近距離
        +---------------+------------------+
                        | 非同期レプリケーション
        +---------------v------------------+
        |       災害復旧(DR)センター          |  <- 遠隔地、定期切替訓練
        +-----------------------------------+

元帳DBの同期レプリケーション区間はデータ無損失(RPO 0)を狙う区間であり、DRセンターは地域災害への備えです。韓国の電子金融監督規程では災害復旧体制と目標復旧時間に関する要求があるため、重要業務のRTO/RPO目標を定義し、定期的な切替訓練を実施するのが一般的です。

代表的な障害シナリオ

シナリオ症状対応
元帳DBのフェイルオーバー数秒から数十秒の取引失敗自動フェイルオーバー、チャネル再試行ポリシー、未決取引の照合
対外回線の断絶他行振込が全件タイムアウト回線二重化の切替、保留処理後の一括照合
ホットアカウントのロック殺到特定口座の取引遅延が伝播キューイング/スロットリング、当該商品の取引分離
バッチ遅延営業開始前に締め未完了バッチ再起動ポイントの管理、開始必須の先行処理を優先
重複電文の流入同一取引の二重処理リスク取引固有番号ベースの冪等処理

冪等処理を一言でまとめると「同じ取引固有番号の取引は2回届いても1回だけ反映し、2回目には1回目の結果を返す」です。取引明細テーブルの取引固有番号ユニークインデックスが最後の防衛線になります。

テスト戦略

勘定系のテストは「コードが動くか」ではなく「お金が合っているか」を検証しなければなりません。

  1. 整合性の自動検証: 各テストシナリオ後に (a) 取引明細の合計と残高変動の一致、(b) 取引後残高チェーンの無欠性、(c) 元帳と会計仕訳の照合を自動チェックするアサーションを共通化します。
  2. 同時実行テスト: 同じ口座に入金と出金を同時に数百スレッドで浴びせ、最終残高が算術的に正確かを検証します。残高不足による拒否件数まで合算して一致しなければなりません。
  3. 日付境界テスト: 締め直前・締め中・締め直後の取引が正しい計上日を受け取るか、日積数が揺らがないかをシナリオとして固定します。システム日付を注入可能にしておくことが前提条件です。
  4. 障害注入: コミット直前のAP強制終了、対外応答の遅延、重複電文の再送といったケースを自動化し、補償・照合ロジックを検証します。
  5. 移行の照合: 次世代・移行プロジェクトであれば、新旧システムに同じ取引を流して元帳を全件比較する並行テストが事実上必須です。

用語集

用語英語表記意味
受信(預金業務)Deposits預金を受け入れて管理する業務。銀行の負債サイド
与信(融資業務)Lending貸出業務。銀行の資産サイド
勘定系Core Banking System元帳を保有する記録システム
情報系Information System / DW分析・報告用のデータシステム
対外系External Gateway外部機関との電文連携システム
元帳Ledger口座・残高・取引の公式記録
電文Message / Telegramシステム間の固定フォーマット取引メッセージ
計上日Posting Date会計上、取引が帰属する営業日
日次締めEOD Closing営業日単位の決算・整理バッチ
日積数Daily Accumulated Balance日次残高の累計。利息計算の基礎
他店券Uncleared Items決済が完了していない他行の手形・小切手
照合Reconciliation2つの記録間の一致検証
補償取引Compensating Transaction原取引を相殺する反対取引
休眠預金Dormant Deposits長期未取引により休眠処理された預金

おわりに

預金システムは華やかな技術のショーケースではありません。むしろその逆で、「残高は1ウォンたりとも違ってはならない」という単純な命題を守るために、同時実行制御、不変ジャーナル、冪等処理、補償取引、締めスナップショットといった仕掛けが幾重にも積み上げられた構造物です。新しいアーキテクチャ(MSA、イベントソーシング、クラウド)をコアバンキングに持ち込むときも、この本質は変わりません。技術が変わっても問いは同じです。「この設計でお金が狂いうる経路はどこか?」

次回は預金の華とも言える利息計算エンジンを、日割り計算、優遇金利、税金処理まで掘り下げてみます。

参考資料