Service:ユースケースの順序を表す
Service層はControllerやDB通信の詳細から離れ、1つのユースケースで何をどの順番に行うかを表します。PaymentApplicationServiceが薄い理由を読み解きます。
Java / Spring Bootを入口に、金融システムのService・Repository・Transaction設計を読む。
先に学ぶ内容 — Prerequisite: ソフトウェア工学 / 銀行システム学ぶこと — LEARNING GOALS
このページで学ぶこと
- application serviceの責任
- domain modelへの変換
- 薄いServiceと貧弱なServiceの違い
体験36 · JAVA SERVICE(順序を表す)
Service層が薄いのは、手を抜いているからか確かめよう
まずは予想をひと言。本文を読んだ後、教材内の参照元で実際のServiceの実装を確認します。
- 01まず予想する — PREDICT
まずここだけ:思ったことをひと言書く
Service層が薄く実装されているのは、手を抜いているからだと思いますか?
正解でなくて大丈夫です。「〜だと思う」から始めてみましょう。
ヒント1を見る
『薄い』ことは、常に手抜きを意味するでしょうか?
ヒント2を見る
残高やKYCの最終判断は、どこにあるべきでしょうか?
もっと詳しく:この問いで使う専門用語
- Application Serviceは、ユースケースの処理順序をまとめる層です。
- Commandは、ユースケースへ渡す入力をまとめた型です。
- 薄いServiceは、責任を別の正式な境界へ委ね、必要な順序だけを持つServiceです。
日常語で理解する — PLAIN LANGUAGE
まず、ここだけつかむ
ServiceはHTTPやJSONの詳細から離れ、1つのユースケースで何を行うかを表します。
PaymentApplicationServiceは入力金額をMoneyへ変換し、RPC用の値を組み立ててGatewayを呼びます。
業務規則がDB RPCにあるため、Javaへ同じ判定をコピーせず意図的に薄く保っています。
順番に理解する — DIFFICULT PARTS, STEP BY STEP
難しいポイントを、順番にほどく
専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。
Application Service
ユースケースの処理順序をまとめる層です。
HTTPとDB通信の詳細から業務の流れを切り離すためです。
createPaymentとconfirmOtpを提供します。
Command
ユースケースへ渡す入力をまとめた型です。
HTTP DTOをそのまま内部へ広げないためです。
CreatePaymentCommandが振込依頼の入力です。
薄いService
責任を別の正式な境界へ委ね、必要な順序だけを持つServiceです。
同じ業務規則の二重管理を避けるためです。
KYCや残高はDB RPCが最終判断します。
発展 — ADVANCEDもっと詳しく確認する最初の学習では読まなくても問題ありません。仕組みをもっと深く理解したい場合に開いてください。
ServiceはHTTPを知らず、業務の入口になる
PaymentApplicationServiceはHTTPステータスやJSONを扱いません。入力をMoneyとRequestTransferParamsへ変換し、Gatewayを呼びます。
これによりControllerは通信契約、Serviceはユースケース、Gatewayは外部システム呼び出しへ集中できます。
public PendingPayment createPayment(
String bearerToken,
CreatePaymentCommand command,
UUID idempotencyKey) {
Money amount = Money.ofYen(command.amount());
// RPC用パラメータへ変換してGatewayを呼ぶ
}現在のServiceが意図的に薄い理由
KYC、口座状態、残高、冪等性、排他ロック、台帳記帳は既存PostgreSQL RPCが一貫して判断します。Javaへ同じ規則をコピーすると、片方だけ変更された時に結果が食い違います。
薄いこと自体が良いのではありません。正式な業務判断をDBへ残すというアーキテクチャ判断に沿って薄くしています。
Command・Domain・Responseを分ける
| 型 | 信頼範囲 | 役割 |
|---|---|---|
| CreatePaymentRequest | 外部入力 | 形式を検証する |
| CreatePaymentCommand | ControllerからService | ユースケース入力を表す |
| Money | 検証済みドメイン値 | 整数円の不変条件を守る |
| PendingPayment | RPCの業務結果 | OTP待ち状態を表す |
| CreatePaymentResponse | 外部出力 | 公開してよい形へ整える |
Serviceを変更する時のレビュー観点
- 同じ業務規則がJavaとDBへ二重定義されていないか
- 途中失敗した時に何が残るか
- 外部呼び出しの再試行で二重処理にならないか
- tokenや秘密をログへ出していないか
- ドメイン結果とHTTP表現が混ざっていないか
体験学習の続き · LEARNING LOOP
ここまで読めましたか?続けて確認してみましょう
Application ServiceとThin Serviceの考え方を読んだところで、教材内の参照元で実際のServiceの実装を確認し、自分の言葉で説明してみます。
- 02仕組みを確かめる — CONCEPT CHECK
画面操作なしで、仕組みを確かめる
この仕組みは、Mini Bankの画面には現れません。次のEvidenceとCode Trail、教材本文だけで理解を進めます。
- 03証拠を確認する — EVIDENCE
何を見たか、1つずつ確かめる
Evidence(判断の根拠となる記録)を確認したらチェックします。各項目の「何を見るか」を手がかりにしてください。
画面を開けない項目は、Source Trail(教材の参照元)と教材の説明で確認できます。失敗を実際に起こす必要はありません。
- 04コードの処理経路を追う — CODE TRAIL
コードを書く必要はありません
ここでは「説明されている仕組みが、実際のMini Bankのどこに実装されているか」を確認します。まずは日本語の役割説明を読み、入力した依頼がどこへ渡るかをつかみましょう。コードの全文を読む必要はありません。
まずServiceが何を扱い何を扱わないかを見て、次にHTTPから切り離された入力の型を見ます。
教材の参照元を確認する — Source Trail ↓振込依頼とOTP確認のオーケストレーション。
services/payment-service/src/main/java/com/minibank/payment/application/PaymentApplicationService.javaHTTPから切り離したユースケース入力。
services/payment-service/src/main/java/com/minibank/payment/application/CreatePaymentCommand.java - 05自分の言葉で説明する — EXPLAIN
LEVEL 1 — やさしい説明(まずここだけ)
なぜPaymentApplicationServiceは、残高やKYCの判定を自分で持たずDB RPCへ委ねているのでしょうか? 短い2文を目安に説明してください。
日常語だけで完了できます。専門用語の数や正誤を自動採点するものではありません。
ヒント1を見る
「薄いService」という言葉を使って書いてみましょう。
ヒント2を見る
同じ業務規則を2箇所で持つと何が困るか考えてみましょう。
もっと詳しく:LEVEL 2 — 専門用語を使った説明(任意)
Application Service、Command、薄いServiceの3つを使って、Service層の責任を説明してみましょう。
挑戦する場合は、上の説明欄に追記できます。追加しなくても完了できます。
説明の確認ポイント — Checkpoint- 薄いServiceは手抜きではなく設計判断だと説明できる
- 正式な業務判断の場所が1つに保たれていると説明できる
理解を確かめる — CHECK YOUR UNDERSTANDING
3問で理解チェック
答えを頭の中で考えてから、各項目を開いて確認してください。
Q1Serviceが薄ければ常に良い設計ですか?
いいえ。どこが正式な業務判断を持つかという設計理由が必要です。
Q2ServiceはHTTPステータスを決めますか?
現在の構成ではControllerが決め、Serviceはドメイン結果を返します。
Q3Javaでも残高検査をすればDBの検査を削除できますか?
できません。同時実行で値が変わるため、更新と同じDB transaction内の最終検査が必要です。
完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。
教材の参照元 — SOURCE TRAIL
根拠と関連教材
CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。
確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。
- 現在の実装 — CURRENT IMPLEMENTATION
services/payment-service/src/main/java/com/minibank/payment/application/PaymentApplicationService.java振込依頼とOTP確認のオーケストレーション - 現在の実装 — CURRENT IMPLEMENTATION
services/payment-service/src/main/java/com/minibank/payment/application/CreatePaymentCommand.javaHTTPから切り離したユースケース入力 - 現在の実装 — CURRENT IMPLEMENTATION
services/payment-service/src/main/java/com/minibank/payment/domain/Money.javaServiceが生成する金額値