発展LESSON 17
この教材で学ぶこと 責務と依存を整理し、testで守りながら構造を改善します。設計とリファクタリング
対象読者: 動くコードを、変更しやすく安全な構造へ育てたい人。
この教材でできるようになること
- 責務、凝集度、結合度を変更理由から説明できる
- domain logicとI/Oを分離できる
- testで振る舞いを守りながら小さくrefactorできる
- abstractionを導入する時期と、導入しない判断ができる
1. 良い設計は変更を局所化する
設計の目的はclass数を増やすことではありません。要件変更が来たとき、どこを直せばよいか分かり、無関係な場所を壊しにくい構造を作ることです。
次を観察します。
- 一つの関数が複数の理由で変わる
- 同じ判断が複数箇所に重複する
- 外部serviceがないとcore ruleをtestできない
- 名前から副作用や失敗条件が分からない
- 小さな変更で多数のmoduleが連鎖的に変わる
2. domain logicとI/Oを分ける
def calculate_total(prices: list[int], tax_rate: float) -> int:
if any(price < 0 for price in prices):
raise ValueError("price must be non-negative")
if not 0 <= tax_rate <= 1:
raise ValueError("tax_rate must be between 0 and 1")
return round(sum(prices) * (1 + tax_rate))
この関数はHTTP、DB、画面を知りません。adapterが外部入力を変換し、domain functionへ渡します。
HTTP / CLI / UI
↓ adapter
application use case
↓
domain logic
↑ interface
DB / external API
3. 名前で境界を明らかにする
process()、handle()、dataだけでは意図が分かりません。
validate_registration_requestcalculate_discounted_totalsave_learning_progressfetch_lesson_catalog
名前が長くなりすぎる場合、関数の責務が広すぎる可能性があります。
4. 小さくrefactorする
安全な順序:
- 現在の振る舞いをtestで固定する
- 名前変更や関数抽出を一種類だけ行う
- testを実行する
- 差分を読む
- 次の小さな変更へ進む
refactoring中に新機能を混ぜると、失敗原因が構造変更か仕様変更か分かりにくくなります。
5. abstractionを急がない
似て見える二つの処理でも、将来別々に変わるなら統合しないほうがよい場合があります。
抽象化する前に確認します。
- 本当に同じbusiness ruleか
- 重複は何回あり、どのように変化したか
- 呼び出し側は同じ契約を必要としているか
- 新しい抽象化の名前をdomain用語で説明できるか
- 削除や差し替えが容易か
6. dependency direction
core ruleがframeworkの型やDB clientへ直接依存すると、technology変更がdomain全体へ広がります。外側が内側へ依存する形を目指します。
ただし小さなscriptで何層ものinterfaceを作る必要はありません。変更頻度、寿命、team規模、testの必要性に合わせます。
7. review checklist
- 入力境界でvalidationしているか
- function名と戻り値から責務が分かるか
- errorを握りつぶしていないか
- logへ秘密情報を出していないか
- 時刻、乱数、networkをtestで差し替えられるか
- 変更に対応するtestがあるか
- rollback可能な単位か
演習: 一つの巨大関数を分ける
「JSONを読む → 入力検証 → 合計計算 → DB保存 → メール送信」を一関数で行うcodeを想定し、次へ分けてください。
- domain valueとvalidation
- 合計を求めるpure function
- application use case
- storage interface
- notification interface
- CLIまたはHTTP adapter
各境界で起きるerrorと、どこで利用者向けmessageへ変換するかも決めます。
理解チェック
- 変更理由から責務を分けられる
- core logicを外部I/Oなしでtestできる
- 振る舞いを維持した小さなrefactorができる
- 不要なabstractionを作らない理由を説明できる
次に進む
ここまでの教材から一つの小さな作品を完成させます。CLI、静的Webページ、APIのどれかを選び、README、入力検証、test、実行手順、制約を揃えて公開できる形へ仕上げてください。