Upgrade to Pro — share decks privately, control downloads, hide ads and more …

古き良き Laravel のシステムは関数型スタイルでリファクタできるのか

古き良き Laravel のシステムは関数型スタイルでリファクタできるのか

PHPカンファレンス小田原2025での登壇スライドです。
12:15〜「古き良き Laravel のシステムは関数型スタイルでリファクタできるのか」ひがき(@higaki_program)

本スライドでは、約10年間事業を支えてきた Laravel 製営業支援システムの技術負債の解消に「関数型ドメインモデリング」を活用した事例を紹介します。

Tech Leverages

April 10, 2025
Tweet

More Decks by Tech Leverages

Other Decks in Technology

Transcript

  1. | © 2025 Levtech Co., Ltd. 5 「繋げる」を実現する 内製システム 今回の話 LTSF

    に 案件 と IT フリーランス の情報があつまり、マッチングされている 案件 登録 ITフリーランス 営業支援システム LTSF ( LevTech Sales Force Automation )
  2. | © 2025 Levtech Co., Ltd. 6 PHP + 関数型スタイル で負債を低減したい

    今回の話 Scott Wlaschin (著) 猪股 健太郎 (翻訳) 株式会社ドワンゴ 「関数型ドメインモデリング」 LTSF 10年の歴史が積み重なった 古き良き MVC システムに 関数型スタイルを適用して 改善することをめざす
  3. | © 2025 Levtech Co., Ltd. 7 レバテック開発部 ひがき HIGAKI ぺちおだ2025のスタッフです!

    本日はあじの部屋にいることが多いです! LIKE:筋トレ・ポーカー・Switch2(予約済)
  4. | © 2025 Levtech Co., Ltd. 9 「案件推薦」がターゲット LTSF|改善のターゲット LTSF この案件どうですか?

    「案件推薦」 案 件 企業とエンジニアに提案してから決定するかどうかまで その進捗情報はひとつのレコードで管理される… 案 件 案 件
  5. | © 2025 Levtech Co., Ltd. 10 商談フェーズ 登録フェーズ 合意フェーズ 提案フェーズ

    「案件推薦」の状態遷移 LTSF|改善のターゲット 初期 状態 色々 ある 企業 承諾 双方 承諾 色々 ある NG 企業 に提案 エンジニア に推薦 エンジニアが承諾する エンジニアが書類確認する … 面談する オファーする … 推薦を希望 する … 状態数は25個ほど!
  6. | © 2025 Levtech Co., Ltd. 11 ひとつのレコードで状態を管理 LTSF|改善のターゲット id status

    kama boko aji 1 1 hoge _ _ … id status kama boko aji 1 2 fuga 5 _ … 案件推薦 Model (MVC) 約 120 の Nullable カラムの 神テーブル&神モデル 😨 状態(status) によって値が 入力されたりされなかったり…
  7. | © 2025 Levtech Co., Ltd. 12 ひとつのエンドポイントで状態を管理 LTSF|改善のターゲット Request クラスに状態遷移の

    ビジネスロジックが集中 約 1300 行の バリデーション 😨 もろもろの情報更新も同時に実行
  8. | © 2025 Levtech Co., Ltd. 13 POINT 01 ひとつのエンドポイント POINT

    02 Request クラスにビジネスロジックが集中 POINT 03 辛いポイント ひとつのテーブル
  9. | © 2025 Levtech Co., Ltd. 14 一気にすべてを解決できれば理想だけど… "刻むんだ" 目の前の1段を登るために必要な要素を 1段の中でさらに刻んで

    自分が登れる小さいステップを作るんだ その行動を努力と呼ぶ 葦原大介『ワールドトリガー』第247話(集英社)より引用
  10. | © 2025 Levtech Co., Ltd. 15 POINT 01 ひとつのエンドポイント POINT

    02 Request クラスにビジネスロジックが集中 POINT 03 解決のステップを刻む ひとつのテーブル 今回はこの部分を改善する!
  11. | © 2025 Levtech Co., Ltd. 16 つまり LTSF|改善のターゲット リクエストバリデーション処理をドメイン層に移動させたい Scott

    Wlaschin (著), 猪股 健太郎 (翻訳) 株式会社ドワンゴ 「関数型ドメインモデリング」 3.5.1 オニオンアーキテクチャより引用
  12. | © 2025 Levtech Co., Ltd. 17 商談フェーズ 登録フェーズ 合意フェーズ 提案フェーズ

    (再掲)ひとつのエンドポイントで実行される状態遷移 LTSF|改善のターゲット 初期 状態 色々 ある 企業 承諾 双方 承諾 色々 ある NG 企業 に提案 エンジニア に推薦 エンジニアが承諾する エンジニアが書類確認する … 面談する オファーする … 推薦を希望 する …
  13. | © 2025 Levtech Co., Ltd. 18 状態ごとの情報更新/状態遷移でわけて考える LTSF|改善のターゲット 初期 状態

    色々 ある 企業 承諾 双方 承諾 色々 ある NG 企業 に提案 エンジニア に推薦 エンジニアが承諾した エンジニアが書類確認した … 面談した オファーした … 推薦を希望 した … 情報更新 状態遷移 状態遷移できる条件
  14. | © 2025 Levtech Co., Ltd. 19 さらにターゲットを 1 項目のみに刻む LTSF|改善のターゲット

    初期 状態 色々 ある 企業 承諾 双方 承諾 色々 ある NG 企業 に提案 エンジニア に推薦 エンジニアが承諾した エンジニアが書類確認した … 面談した オファーした … 推薦を希望 した … 情報更新 状態遷移 状態遷移できる条件
  15. | © 2025 Levtech Co., Ltd. 21 まずは動作が変わっていないことを保証するためのテストを書きたい が、リクエストバリデーションにロジックが集中しているため統合テストしか書けない… 統合テストは正常系のみ検査したい(実行時間との兼ね合い) が、統合テストでしか準正常系のテストが書けない…

    リファクタ方針 やっていき 実行時間との兼ね合い?? ▪ 統合テストではDBも含めてテストを行う ▪ IOに時間がかかる Eloquentのモデルイベントを多用しているため、 0からモデルイベントを発火させる必要がある LTSF DB
  16. | © 2025 Levtech Co., Ltd. 22 リファクタ方針 やっていき 1. 統合テストに正常系・準正常系のテストを追加

    2. ドメイン層にロジックを移行 3. 統合テストをユニットテストに移行し、統合テストの準正常系のテストを削除 Scott Wlaschin (著), 猪股 健太郎 (翻訳) 株式会社ドワンゴ「関数型ドメインモデリング」より引用 テスト自体も ロジックとともに移行させる
  17. | © 2025 Levtech Co., Ltd. 23 ステップ やっていき 1. 正常系・準正常系の統合テスト追加

    2. ドメイン層でロジックを実現 3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除
  18. | © 2025 Levtech Co., Ltd. 24 ステップ やっていき 1. 正常系・準正常系の統合テスト追加

    2. ドメイン層でロジックを実現 3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除
  19. | © 2025 Levtech Co., Ltd. 25 1. 正常系・準正常系の統合テスト追加 ▽ 正常系のテスト

    遷移条件を全て満たす 企業承諾 の案件推薦を作成 → 双方承諾 に遷移できること確認 ▽ 準正常系のテスト 未確認の書類が存在する 企業承諾 の案件推薦を作成 → 双方承諾 に遷移できないことを確認 やっていき 企業 承諾 双方 承諾 エンジニアが書類確認済
  20. | © 2025 Levtech Co., Ltd. 26 1. 正常系・準正常系の統合テスト追加 やっていき コード載せる

    統合テスト 追加!  約750行 • Eloquentのモデルイベントを多用しているため、 0からモデルイベントを発火させる必要がある • DBレコードの更新を何度も行う必要があるため、 IOの処理を何度も行っており時間もかかる… ほとんどはテスト対象の準備 🥲
  21. | © 2025 Levtech Co., Ltd. 27 ステップ やっていき 1. 正常系・準正常系の統合テスト追加

    2. ドメイン層でロジックを実現 3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除
  22. | © 2025 Levtech Co., Ltd. 28 ビジネスロジック 企業承諾 → 双方承諾

    の遷移には「エンジニアが書類確認済」が条件 2. ドメイン層でロジックを実現 やっていき 企業 承諾 双方 承諾 エンジニアが書類確認済
  23. | © 2025 Levtech Co., Ltd. 31 1⃣ デシリアライズ APIリクエストからDTOを作成 -

    Godモデル と 双方承諾モデル のみ作成 - ドメインモデルを作りきらず、今回必要なドメインプロパティのみに限定 2. ドメイン層でロジックを実現 やっていき
  24. | © 2025 Levtech Co., Ltd. 32 2⃣ IO処理 状態遷移判定に必要な情報をDBから取得 -

    状態遷移関数内の副作用を避けるため、DBアクセスした結果を状態遷移関数に渡す 2. ドメイン層でロジックを実現 やっていき
  25. | © 2025 Levtech Co., Ltd. 33 3⃣ 状態遷移 企業承諾 →

    双方承諾 の遷移には「エンジニアが書類確認済」が条件 純粋関数として実現する 詳細は次のスライド 2. ドメイン層でロジックを実現 やっていき Godモデル 書類 双方承諾モデル ドメインエラー 純粋 関数
  26. | © 2025 Levtech Co., Ltd. 34 ▪ 純粋なPHPですべて実装されおり、入力が決まれば、出力が一意に決まる ▪ 全ての条件をチェックする

    2.1. 状態遷移 やっていき Godモデル 書類 契約書類が 確認済 WorkflowErrorを追加 Workflow Errorが 存在しない No 双方承諾モデル ドメインエラー No このブロックを追加していくことで ロジックの移行を進められる 今回移行するロジック
  27. | © 2025 Levtech Co., Ltd. 35 「関数型ドメインモデリング」で参考にしたこと ▪ 内部を見なくても、予測可能で理解しやすい関数にする ▪

    I/Oを端に追いやる 副作用を避けるためDBアクセスの結果を状態遷移の関数に渡した 2. ドメイン層でロジックを実現 やっていき Scott Wlaschin (著) 猪股 健太郎 (翻訳) 株式会社ドワンゴ 「関数型ドメインモデリング」 Godモデル 書類 双方承諾モデル ドメインエラー 純粋 関数
  28. | © 2025 Levtech Co., Ltd. 36 ステップ やっていき 1. 正常系・準正常系の統合テスト追加

    2. ドメイン層でロジックを実現 3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除
  29. | © 2025 Levtech Co., Ltd. 37 3. ドメイン層の正常系・準正常系のユニットテスト追加 ▽ 正常系のテスト

    - 入力(Godモデルと確認済みの書類) - 期待の出力(双方承諾モデル) ▽ 準正常系のテスト - 入力(Godクラスと未確認の書類) - 期待の出力(ドメインエラー) やっていき Godモデル 書類 双方承諾モデル 純粋 関数 Godモデル 書類 ドメインエラー 純粋 関数
  30. | © 2025 Levtech Co., Ltd. 38 コード載せる 統合テスト  約 750

    行 3. ドメイン層の正常系・準正常系のユニットテスト追加 やっていき ユニットテスト  約 30 行 🎉
  31. | © 2025 Levtech Co., Ltd. 39 嬉しいポイント 😊 ▪ テストの準備が楽になった

    - DBにデータを作らなくても良くなった → IO処理がなくなった - 複雑なバリデーションを通す必要がなくなった ▪ テストの実行時間が短くなった - ビジネスロジックのテストはユニットテストに寄せていきたい 3. ドメイン層の正常系・準正常系のユニットテスト追加 やっていき
  32. | © 2025 Levtech Co., Ltd. 40 1. 正常系・準正常系の統合テスト追加 2. ドメイン層でロジックを実現

    3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除 ステップ やっていき
  33. | © 2025 Levtech Co., Ltd. 42 ステップ やっていき 1. 正常系・準正常系の統合テスト追加

    2. ドメイン層でロジックを実現 3. ドメイン層の正常系・準正常系のユニットテスト追加 4. 元々のロジック削除 5. 準正常系の統合テスト削除
  34. | © 2025 Levtech Co., Ltd. 46 関数型スタイルにより享受できたメリット リファクタを通して ▪ 状態と振る舞いを分離

    「モデル」 と 「モデルへの遷移」 で関心ごとを分離できた 今回の対象のビジネスロジックを「モデルへの遷移」の条件として表現できた ▪ ドメイン層(状態遷移関数)が参照透過性を満たす IOを端に追いやったことで実現できた ▪ 内部を見なくても、予測可能で理解しやすい関数にする Godモデル 書類 双方承諾モデル ドメインエラー 純粋 関数
  35. | © 2025 Levtech Co., Ltd. 47 振り返り ▪ Result型(クラス)を作ればもう少し整理して書けたかも  

    PHPでResult型デフォルトであったら使ってた ▪ PHPで関数型を再現する場合、クラスを大量に作ることになりそう   ある集合を作ろうと思った場合、PHPではクラスで表現することが多い ▪ 0から理想のコードを作るより、   今あるコードをどう理想に近づけるかの面白さを学んだ(大変だけど) リファクタを通して
  36. | © 2025 Levtech Co., Ltd. 49 古き良き Laravel のシステムは 関数型スタイルでリファクタできるのか

    まとめ 現状と理想のギャップを段階的に整理し、 局所的に関数型スタイルを取り込むことでリファクタできるかも