学習・技術検証用デモ — 本システムは実際の銀行サービスではありません。

公開情報をもとに構築した教育目的のシステムです。実在する個人情報・口座情報・機密情報・認証情報等を入力しないでください。

ミニ銀行ACADEMY SYSTEM LEARNING
教材メニュー
学習進捗0 / 134
あと134項目
INTERMEDIATE · 中級COURSE 05 · JAVA / ENTERPRISE DEVELOPMENT

Repository:PostgreSQL RPCとの境界

このPayment ServiceのRepository層はJPAでテーブルを操作しません。既存PostgREST RPCを呼ぶ薄いGatewayとして、JavaとPostgreSQLの責任境界を維持します。

05 / 11
JAVA / ENTERPRISE DEVELOPMENT · Java・エンタープライズ開発

Java / Spring Bootを入口に、金融システムのService・Repository・Transaction設計を読む。

先に学ぶ内容 — Prerequisite: ソフトウェア工学 / 銀行システム

学ぶこと — LEARNING GOALS

このページで学ぶこと

  • RepositoryとGatewayの考え方
  • Spring RestClientによるRPC呼び出し
  • SQLSTATEを安全なAPIエラーへ変換する方法

体験37 · JAVA REPOSITORY(DBとの境界)

Payment ServiceはJPAやJDBCでテーブルを直接読み書きしているか確かめよう

まずは予想をひと言。本文を読んだ後、教材内の参照元で実際のGatewayとエラー変換を確認します。

体験学習 · STEP 1 / 5
  1. 01
    まず予想する — PREDICT

    まずここだけ:思ったことをひと言書く

    Payment ServiceはJPAやJDBCで直接テーブルを読み書きしていると思いますか?

    正解でなくて大丈夫です。「〜だと思う」から始めてみましょう。

    ヒント1を見る

    既存の振込RPCは、どこにありましたか?

    ヒント2を見る

    JavaからDBへ直接接続する経路と、既存RPCを呼ぶ経路の違いは何でしょうか?

    もっと詳しく:この問いで使う専門用語
    • Gatewayは、外部システムとの通信を内部モデルから隠す境界です。
    • PostgREST RPCは、HTTPからPostgreSQL関数を呼ぶ入口です。
    • SQLSTATEは、DBエラーを機械的に識別するcodeです。
まずはひと言だけで大丈夫です。

この後の本文を最後まで読んでから、実際に操作して確かめるステップへ進みます。

日常語で理解する — PLAIN LANGUAGE

まず、ここだけつかむ

1

Payment ServiceのRepository層はテーブルを直接保存せず、既存PostgreSQL RPCをPostgREST経由で呼びます。

2

呼出者のBearer tokenをそのまま転送し、既存のauth.uid()、RLS、関数権限を維持します。

3

DBのSQLSTATEを安定したAPI codeと安全なメッセージへ変換し、未知の内部情報は外へ漏らしません。

順番に理解する — DIFFICULT PARTS, STEP BY STEP

難しいポイントを、順番にほどく

専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。

01

Gateway

やさしく言うと

外部システムとの通信を内部モデルから隠す境界です。

なぜ必要?

URLやJSON形式をServiceへ漏らさないためです。

Minibankでは

PaymentRpcGatewayがRPC引数と結果を変換します。

02

PostgREST RPC

やさしく言うと

HTTPからPostgreSQL関数を呼ぶ入口です。

なぜ必要?

既存のDB関数を正式な業務境界として再利用するためです。

Minibankでは

/rpc/request_transferなどへPOSTします。

03

SQLSTATE

やさしく言うと

DBエラーを機械的に識別するcodeです。

なぜ必要?

人向け文言に依存せず、安全な公開エラーへ変換するためです。

Minibankでは

TR010をINSUFFICIENT_FUNDSへ変換します。

発展 — ADVANCEDもっと詳しく確認する最初の学習では読まなくても問題ありません。仕組みをもっと深く理解したい場合に開いてください。

なぜJPAやJDBCを使わないのか

既存Minibankでは、振込の正式な入口がPostgreSQLのrequest_transfer()とconfirm_transfer_otp()です。Javaからテーブルを直接更新すると、RLS、関数権限、ロック順序、台帳記帳、監査を迂回する危険があります。

PaymentRpcGatewayは既存RPCへ必要な引数を渡し、返却行をドメインモデルへ変換する役割だけを持ちます。

RPC呼び出しの流れ

  1. 01Gatewayがp_*形式の引数Mapを作る
  2. 02SupabaseRpcClientがPOST /rpc/{fn}を呼ぶ
  3. 03publishable keyと呼出者のAuthorizationを送る
  4. 04PostgRESTがtokenを解釈しPostgreSQL関数を実行
  5. 05JSON配列をMapへ読み取る
  6. 06GatewayがPendingPaymentまたはPaymentOtpConfirmationへ変換

Bearer tokenを引き継いで認可文脈を保つ

Javaサービス自身の強い権限でRPCを呼ぶのではなく、利用者のBearer tokenを転送します。そのためauth.uid()と既存RLS・RPCの認可判断を引き続き使えます。

これはtokenを信頼するという意味ではありません。有効性はPostgREST側で検証され、Javaは形式と伝搬を担当します。

restClient.post()
    .uri("/rpc/{fn}", functionName)
    .header(HttpHeaders.AUTHORIZATION, bearerToken)
    .body(params)
    .retrieve();

DBエラーを公開契約へ翻訳する

PostgreSQL関数はTR003やTR010などのSQLSTATEで失敗理由を表します。RpcErrorMapperがそれをApiErrorCodeと安全な日本語メッセージへ変換します。

未知のSQLSTATEやJSONとして読めない応答はINTERNAL_ERRORへ閉じます。内部メッセージをそのまま返さないことが、情報漏えいと不安定なAPI契約を防ぎます。

SQLSTATEAPI code意味
TR003KYC_NOT_VERIFIED本人確認未完了
TR009IDEMPOTENCY_KEY_CONFLICT同じキーで異なる依頼
TR010INSUFFICIENT_FUNDS残高不足
未知INTERNAL_ERROR内部詳細を公開しない

体験学習の続き · LEARNING LOOP

ここまで読めましたか?続けて確認してみましょう

GatewayとSQLSTATEの考え方を読んだところで、教材内の参照元で実際のGatewayとエラー変換を確認し、自分の言葉で説明してみます。

STEP 2〜5 / 5
  1. 02
    仕組みを確かめる — CONCEPT CHECK

    画面操作なしで、仕組みを確かめる

    この仕組みは、Mini Bankの画面には現れません。次のEvidenceとCode Trail、教材本文だけで理解を進めます。

  2. 03
    証拠を確認する — EVIDENCE

    何を見たか、1つずつ確かめる

    Evidence(判断の根拠となる記録)を確認したらチェックします。各項目の「何を見るか」を手がかりにしてください。

    画面を開けない項目は、Source Trail(教材の参照元)と教材の説明で確認できます。失敗を実際に起こす必要はありません。

  3. 04
    コードの処理経路を追う — CODE TRAIL

    コードを書く必要はありません

    ここでは「説明されている仕組みが、実際のMini Bankのどこに実装されているか」を確認します。まずは日本語の役割説明を読み、入力した依頼がどこへ渡るかをつかみましょう。コードの全文を読む必要はありません。

    まずGatewayが既存RPCをどう呼ぶかを見て、次にDBエラーがどう安全なAPIエラーへ変換されるかを見ます。

    RPC引数とドメインモデルの変換。

    services/payment-service/src/main/java/com/minibank/payment/repository/PaymentRpcGateway.java

    SQLSTATEとAPIエラーの対応。

    services/payment-service/src/main/java/com/minibank/payment/error/RpcErrorMapper.java
    教材の参照元を確認する — Source Trail ↓
  4. 05
    自分の言葉で説明する — EXPLAIN

    LEVEL 1 — やさしい説明(まずここだけ)

    なぜPayment ServiceはJPAでテーブルを直接操作せず、既存のPostgreSQL RPCを呼び出す設計にしているのでしょうか? 短い2文を目安に説明してください。

    日常語だけで完了できます。専門用語の数や正誤を自動採点するものではありません。

    ヒント1を見る

    「Gateway」という言葉を使って書いてみましょう。

    ヒント2を見る

    既存RPCを迂回すると何を失うか考えてみましょう。

    もっと詳しく:LEVEL 2 — 専門用語を使った説明(任意)

    Gateway、PostgREST RPC、SQLSTATEの3つを使って、RepositoryとDBの境界を説明してみましょう。

    挑戦する場合は、上の説明欄に追記できます。追加しなくても完了できます。

    説明の確認ポイント — Checkpoint
    • 既存RPCの認可・lock・記帳経路を迂回していないと説明できる
    • 内部DBの詳細が外部契約へ漏れないと説明できる
予想5文字以上・証拠2項目・説明20文字以上で完了できます。

証拠の確認:0 / 2。LEVEL 2(専門用語での説明)は任意です。

入力はこのブラウザに保存されます。Academy教材の進捗とは別の学習メモです。

理解を確かめる — CHECK YOUR UNDERSTANDING

3問で理解チェック

答えを頭の中で考えてから、各項目を開いて確認してください。

Q1なぜJPAでtransfersへ直接INSERTしないのですか?
答え

既存RPCの認可、lock、transaction、Ledger、監査を迂回する可能性があるためです。

Q2Javaはservice roleでRPCを呼びますか?
答え

いいえ。publishable keyと呼出者のBearer tokenを使います。

Q3未知のSQLSTATEをそのまま利用者へ返しますか?
答え

返しません。INTERNAL_ERRORへ閉じ、生のDBメッセージを隠します。

ここまで読めましたか?

完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。

教材の参照元 — SOURCE TRAIL

根拠と関連教材

CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。

確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。

  • 現在の実装 — CURRENT IMPLEMENTATIONservices/payment-service/src/main/java/com/minibank/payment/repository/PaymentRpcGateway.javaRPC引数とドメインモデルの変換
  • 現在の実装 — CURRENT IMPLEMENTATIONservices/payment-service/src/main/java/com/minibank/payment/repository/SupabaseRpcClient.javaPostgRESTへのHTTP通信
  • 現在の実装 — CURRENT IMPLEMENTATIONservices/payment-service/src/main/java/com/minibank/payment/error/RpcErrorMapper.javaSQLSTATEとAPIエラーの対応