1. test 1
  2. test 2
  3. test 3
  4. test 4
  5. test 5
  6. test 6
  7. test 7
  8. test 8
コース一覧
Spring Boot入門
料金計算のような業務ロジック

Spring Boot入門

Java で Web API サーバーを作るための Spring Boot の入門コースです。DI コンテナ、REST コントローラ、リクエスト検証、レイヤ分割、Spring Data JPA、例外ハンドリング、テストまでを扱います。毎レッスン、Spring が裏側でやっている判断そのものを pure Java の関数として自分で書き、フレームワークが何を肩代わりしてくれているのかを手触りで理解できる構成にしました。約 12 時間 (1 日 30 分 × 24 日) で 48 レッスンを修了できます。

1
Spring Bootとは何か
01. Spring Bootで何が作れるのか10分
02. サーブレットからフレームワークへ15分
03. ルーティング表という発想15分
04. プロジェクトの構成10分
05. 起動のしくみと自動設定15分
06. パスのマッチング15分
2
DIコンテナ
01. newで書くと何が困るのか15分
02. コンストラクタインジェクション15分
03. インターフェースへの依存15分
04. @Componentと@Bean15分
05. 自作のDIコンテナ20分
06. Beanのスコープとライフサイクル15分
3
RESTコントローラ
01. RESTという設計方針15分
02. @RestControllerの役割15分
03. @GetMappingとパス変数15分
04. クエリパラメータの受け取り15分
05. @PostMappingとリクエストボディ15分
06. ステータスコードの選び方15分
07. ResponseEntityで組み立てる15分
4
リクエストとレスポンスの変換
01. JSONとオブジェクトの往復15分
02. DTOとエンティティを分ける15分
03. recordでDTOを書く15分
04. 入力検証の基本15分
05. 検証エラーをまとめて返す15分
06. アノテーションによる検証15分
5
レイヤ構成とビジネスロジック
01. 3層アーキテクチャ10分
02. @Serviceに置くもの15分
03. 状態遷移をルールにする15分
04. 料金計算のような業務ロジック15分
05. @Transactionalとは何か15分
06. トランザクションの巻き戻し15分
6
データアクセス
01. @Entityとテーブルの対応15分
02. リポジトリという抽象15分
03. メソッド名からクエリを作る20分
04. 検索条件の組み立て15分
05. ページングと並び替え15分
06. N+1問題15分
7
例外処理と横断的関心事
01. 例外をHTTPに変換する15分
02. @ExceptionHandlerと@ControllerAdvice15分
03. エラーレスポンスの形を決める15分
04. フィルタとインターセプタ15分
05. ログとリクエストの追跡15分
06. 設定値の外部化15分
8
テストと仕上げ
01. テストの種類と使い分け15分
02. Serviceの単体テスト15分
03. モックで依存を差し替える15分
04. MockMvcでエンドポイントを試す15分
05. 本番へ出すための次の一歩10分

料金計算のような業務ロジック

このレッスンでは、条件がいくつも絡む計算を、後から読んでも仕様が追える形に書く方法を身につけます。

業務ルールは仕様書と同じ順番で書きます

料金計算やポイント付与のようなルールは、条件が3つ4つと重なるとすぐに読めなくなります。次のような書き方は、動いていても直せません。

Java

int price = amount - (type.equals("gold") ? (months >= 12 ? amount * 20 / 100 : amount * 10 / 100) : type.equals("silver") ? (months >= 12 ? amount * 10 / 100 : amount * 5 / 100) : 0);

何が起きているかを読み解くには、括弧を目で数える必要があります。しかも「ゴールドで12か月以上なら20パーセント引き」という仕様の1行が、コードのどこに対応するのかが分かりません。

読める形にする方法は単純です。仕様の一文をコードの一文に対応させることです。

Java

int discountRate(String type, int months) { if ("gold".equals(type)) { return months >= 12 ? 20 : 10; } if ("silver".equals(type)) { return months >= 12 ? 10 : 5; } return 0; }

行数は増えましたが、仕様の表と1対1で見比べられるようになりました。業務ロジックで優先すべきは短さではなく、仕様との照合しやすさです。

名前を付けた中間変数を惜しまない

もう1つの武器が中間変数です。式の途中に意味のある名前を置くと、その名前自体が説明になります。

Java

int rate = discountRate(type, months); int discount = amount * rate / 100; int payable = amount - discount; int point = payable / 100;

payable という名前があるおかげで、ポイントが割引前ではなく割引後の金額から計算されていることが一目で分かります。これを1行にまとめると、その事実がコードから消えます。

端数の扱いは必ず明文化します

金額計算で最も揉めるのが端数です。amount * rate / 100 を int で書くと切り捨てになります。切り上げたいのか、四捨五入したいのか、これは技術の話ではなく業務の決めごとです。曖昧なまま書くと、後で「1円合わない」という調査に何日も溶かすことになります。

書き方結果
a / b (int どうし)0 方向へ切り捨て
Math.floorDiv(a, b)負の値でも小さい方へ切り捨て
(a + b - 1) / b (正の数)切り上げ

実務では通貨計算に BigDecimal と RoundingMode を使い、丸め方を明示するのが定石です。

境界値を必ずテストに入れます

条件に >= が出てきたら、その境目ちょうどの値を必ずテストします。12 か月以上という仕様なら、11 と 12 の両方を確かめます。> と >= の取り違えは、実装した本人には絶対に見つけられない種類の誤りで、境界値のテストだけが捕まえられます。

Java

@Test void ゴールド会員は12か月ちょうどで20パーセント() { assertEquals(20, discountRate("gold", 12)); } @Test void ゴールド会員は11か月なら10パーセント() { assertEquals(10, discountRate("gold", 11)); }

この2本が揃っていれば、> に書き換えた瞬間に片方が落ちます。

Spring での置き場所

こうした計算は @Service を付けたクラスに置きます。Controller に置くとテストが重くなり、エンティティに置くと外部の料金表を参照しづらくなることが多いためです。Service に置いておけば、テストは new して呼ぶだけで済みます。フレームワークの機能を1つも使っていないことが、そのまま利点になります。

演習で書くもの

演習では、会員種別と継続利用月数と利用金額から、請求額と付与ポイントを計算します。境界値がテストに含まれているので、>= と > の違いに注意してください。

要件

  1. 戻り値は rate=<割引率>,payable=<請求額>,point=<付与ポイント> の形にすること
  2. 割引率は gold と silver のみに適用し、12 か月ちょうどは上位の率が適用されること
  3. ポイントは割引前ではなく請求額を 100 で割った商とし、継続 24 か月以上なら 2 倍にすること。amount が 0 以下なら全項目 0 とすること

入出力例

charge("gold", 12, 10000) → "rate=20,payable=8000,point=80" charge("gold", 11, 10000) → "rate=10,payable=9000,point=90" charge("silver", 12, 10000) → "rate=10,payable=9000,point=90" charge("silver", 11, 3000) → "rate=5,payable=2850,point=28" charge("bronze", 30, 5000) → "rate=0,payable=5000,point=100" charge("gold", 24, 10000) → "rate=20,payable=8000,point=160" charge("silver", 23, 1050) → "rate=10,payable=945,point=9" charge("gold", 36, 0) → "rate=0,payable=0,point=0"

ヒント

割引率を返す小さなメソッドを分けて書くと、本体が読みやすくなります

12 か月以上は months >= 12 です。months > 12 と書くと 12 か月ちょうどの結果がずれます

ポイントの 2 倍は、請求額を 100 で割った商を求めた後に掛けます。先に 2 倍してから割ると端数の扱いが変わります

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/09

関連レッスン

  • @Transactionalとは何か

    トランザクション境界とロールバックの条件を確認します。

  • トランザクションの巻き戻し

    失敗時に変更をなかったことにする処理を実装します。

  • @Entityとテーブルの対応

    エンティティのマッピングと主キーの指定を確認します。

  • 例外をHTTPに変換する

    例外の種類からステータスコードを決める処理を実装します。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 変数データに名前をつけて参照する仕組み
  • テストバグを見つける工程
  • クラスデータと振る舞いを束ねる単位
  • エンティティDB の 1 行 = アプリの 1 オブジェクト
  • 戻り値呼び出し元への返答を表す点線矢印
main.java
学習モード
エディタを読み込んでいます

メモ

料金計算のような業務ロジック

⌘S で保存