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

新卒 Laravel 初心者が成長していく中で 感じたコレジャナイ感/PHPerKaigi 2022

新卒 Laravel 初心者が成長していく中で 感じたコレジャナイ感/PHPerKaigi 2022

PHPer Kaigi 2022 の発表資料です.

Laravel って,便利な機能が沢山実装されているし,ディレクトリ構成もテンプレートがそのまま使えて初学者に優しいですよね.
かくいう僕も,ほとんど Laravel の実務経験が無いまま新卒で社内プロダクトを引き継いだ頃は「便利だ便利だ」とLaravelに乗っかりまくっていました.
そんな中,だんだん「これってどうなの」と思う点がチラホラ出てきました.
・Controller と Model だけに処理を記述すると,肥大化して責務が不明瞭になってしまう
・Facade はどこでも使えて便利だが,依存関係が分かりづらくなってテストや拡張がしずらくなってきた
n番煎じかもしれませんが,「Laravel・PHP 初学者だったらこうしがち,でもちょっとできる人はこうやるよ」という観点で,実際に僕が経験した Laravel のコレジャナイ感とその解決法をお話します.

ふわせぐ

April 07, 2022
Tweet

More Decks by ふわせぐ

Other Decks in Programming

Transcript

  1. 自己紹介 $aboutMe = [ ‘📛’ => ‘竹下 拓秀(ふわせぐ)’, ‘🎂’ =>

    CarbonImmutable::parse(‘1998-08-04 03:46:00’), ‘🏠’ => ‘長崎県出身・愛知県在住’, ‘🏢’ => ‘株式会社ゆめみ コーポレートエンジニア(フルサイクル)’, ‘👶’ => ‘2歳(男の子)’, ‘ ’ => ‘@fuwasegu’, ‘ ’ => ‘@lunain84’, ]; PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 2
  2. 正に職人用フレームワーク Laravel の代表的な機能の例 • サービスコンテナ • Eloquent ORM • Event

    / Job • Facade • MVC アーキテクチャのボイラープレート PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 4
  3. 6 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション Laravel

    に従いマニュアルに沿って開発すれば, なんとなく Web アプリが開発できる! \初心者にやさしい!/
  4. 犯人は Laravel 先生!? PHPerkaigi 2022 04/10 12:45 〜 Track B

    スポンサーセッション 10 • 依存関係の混沌化 → Facade のせいで何が何に依存しているか分かりにくくなる • 責務の肥大化 → Controller に処理を詰め込むと当然肥大化.安易に Model に処理を移そうとしても責務不明瞭で失敗しやすい • 重くなるテスト → Laravel の立ち上げ + DB 利用で実行に時間がかかる
  5. 12 どうすればいいの? PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション

    • 依存関係の混沌化 → Facade の使用を極力控え,Constructor で依存注入する • 責務の肥大化 → レイヤー毎に求められる責務を意識してクラスを分割する • 重くなるテスト →ビジネスロジックを Laravel から切り離し UnitTest する
  6. 13 今日話すこと PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション

    'BDBEF ཭ΕΛ͠Α͏ .7$ ʹશͯΛ٧ΊΔͷ͸΍Ίʹ͠Α͏ 6OJU ςετΛॻ͜͏
  7. Facade に出会った僕 • Facade って便利だと思ってた – どこからでも簡単に呼べるじゃん! – どんどん使っていく べきでは?

    • Facade は static メソッドの集まりだと思ってた – Laravel は static メソッドを作るのが主流? PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 16
  8. 思想がインスコされた僕 • Facade はできるだけ使わない方がいい – 乱用すると秩序が乱れる – どこからでも呼べるのがむしろダメ – 部分的に使う分にはOK

    • Route,DB::transaction() とか • 全然 static メソッドじゃなかった PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 18
  9. 19 Facade の仕組み PHPerkaigi 2022 04/10 12:45 〜 Track B

    スポンサーセッション • ただの Static メソッドに見えるけど実は全然違う – Static メソッドとしては存在していない Sample::method() Facade.php __callStatic() Facade.php getFacadeRoot() Facade.php resolveFacadeInstance() $instance = app()-> make(Sample::class) $instance->method() ※ 若干簡略化しています サービスコンテナの機能 = Laravel の立ち上げが必要 実体は static じゃなかった!
  10. 20 Facade を使ったとき PHPerkaigi 2022 04/10 12:45 〜 Track B

    スポンサーセッション コンテナ OK! OK! また? まとめて言えよ FacadeA FacadeB __construct() methodA() methodB() FacadeC FacadeD Aを用意して! Bを用意して! Cを用意して! Dを用意して! FacadeA FacadeB Aを用意して! Bを用意して!
  11. 依存関係を確認したくても PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 22

    • 呼び出し側から実装にジャ ンプできない • Laravel 標準 Facade は @see アノテーションがあるのでまだ マシ • 自作 Facade は明示的に @see を書く or サービスプロ バイダを見に行く必要がある • Facadeの実体 から 別の Facade を呼んだりするといよ いよ依存関係の確認が大変
  12. 24 コンストラクタDIを使ったとき PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション

    __construct(A,B,C,D) methodA() methodB() ご注文の A・B・C・D です 準備完了! ClassD 準備完了! ClassC 準備完了! ClassB 準備完了! ClassA 準備完了! ClassB 準備完了! ClassA
  13. 26 既存 Facade どうするの? PHPerkaigi 2022 04/10 12:45 〜 Track

    B スポンサーセッション 実体をそのまま DI をしよう!
  14. 28 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション Facade

    は 「使用上の注意」をよく読み, 用法用量を守って正しくお使いください. 体に異常がみられた方はすぐにご使用を中断し, 医師にご相談ください.
  15. はじまりの街 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 30

    app Models Console Http Controllers • Laravel プロジェクトを新 規作成すると現れるボイ ラープレート • すでになんか出来上がっ てる?
  16. Laravel のボイラープレート • MVC2 アーキテクチャで出来てる – Controller: HTTP リクエストの受け取り –

    (Eloquent)Model: DB アクセス PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 31 Web Browser Controller (Eloquent)Model View HTTP リクエスト DB 操作の依頼 Database DB 操作 結果の返却 表示に必要なデータの 受け渡し 親の顔より見たアーキテクチャ図 ビジネスロジックをどこに置く? • Controller • Model レンダリング結果 レスポンス (HTML・JSON)
  17. Controller に置くつらみ • HTTP を扱う場所と,DB を扱う場所が同じところにな り,責務が不明瞭になる • HTTPリクエスト起点の大きな機能テストしか書けない –

    一つのテストで考えることが多すぎる • リクエストバリデーション • ドメインバリデーション • ビジネスロジック • レスポンスの整形 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 32
  18. Model に置くつらみ • すべてのビジネスロジックが Model と1対1で対応す るとは限らない • Model から

    外部 API を叩く違和感 • ビジネスロジックに関する例外を置く場所に困る – app/Models/Exceptions は違う – app/Exceptions はなんか遠い... • Eloquent Model が多くのメソッドを持つので,たまにメ ソッド名が衝突して命名に悩む – delete, update など PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 33
  19. 責務分散を助ける Laravel の機能 • Policy – 認可処理の切り出し • FormRequest –

    フォームバリデーションの切り出し • Resource – レスポンス整形処理の切り出し PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 35
  20. • 検索条件が細かいとき – if ($request->has(‘email’)) みたいなのが沢山ある • 外部 API が絡むとき

    – メッセージを生成して Slack へ通知を送る – 弊チームは Slack アプリ開発が多く,このツラミをよく感じ ています 36 それでも太るもんは太る PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  21. どんなアーキテクチャ? • ビジネスロジックの置き場として UseCase を導 入 👍 • UseCase が

    Eloquent Model や Eloquent Builder に 依存することを認めて使いやすく 👍 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 38
  22. • HTTP の知識を剥がした純粋なビジネスロジックを置 く場所が(Models/ 以外で)でき,明確な責務の分割 ができた • Eloquent Model への依存は妥協することで厳格な

    クリーンアーキテクチャより導入コストが低い 39 ”なんちゃって” のここが良い! PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  23. Laravel におけるテストの種類 • Feature テスト – Httpリクエスト単位のテスト – リクエストを受けててレスポンスを返すまでの一連の流れ を検証する

    – 大きな単位のテストなので,時間がかかる • Unit テスト – メソッド単位のテスト – 各々のロジックが正しく動作するかを検証する – 小さな単位のテストなので,あまり時間がかからない PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 43
  24. テストに必要な環境 • Feature テスト – bootstrap() を使って Laravel アプリケーションの立ち 上げが必要

    – DB アクセスを伴うこともある – Illuminate¥Foundation¥Testing¥TestCase を継承する • Unit テスト – PHP が動けば良い – PHPUnit¥Framework¥TestCase を継承で済む PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 44
  25. つまり PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 45

    Feature テストは Laravel に依存するテスト Unit テストは Laravel に依存しないテスト
  26. • ライブラリに切り出しにくい – PHP ライブラリとして切り出すなら大幅なリファクタリングが必要 • 別のフレームワークで使い回せない – フレームワーク固有の機能を使ったコードはコピペでは動 かない

    – この先ずっとそのフレームワークを使いづつける? – もし同じ言語でリプレースするなら,サービスのコアな部分(ビ ジネスロジック)はできるだけそのまま使いまわしたい 46 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション (おまけ)「FW に依存する」ということ
  27. Laravel は基本 Feature テスト • Facade を使用している – サービスコンテナを使うためアプリケーション の起動が必須

    • Eloquent Model / Builder を使用している – DB アクセスを伴うためアプリケーションの起動が 必須 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 47
  28. ”なんちゃって” も Feature テスト PHPerkaigi 2022 04/10 12:45 〜 Track

    B スポンサーセッション 48 mpyw. “5年間 Laravel を使って辿り着いた,全然頑張らない「なんちゃってクリーンアーキテクチャ」という落としどころ”. Zenn, https://zenn.dev/mpyw/articles/ce7d09eb6d8117#テストどうするの? より抜粋
  29. UseCase は Unit テストがしたい • もちろん Feature テストは必須 • しかし網羅性の担保を

    Feature テストに委ねるのは重す ぎる – 細かい分岐それぞれの検証でわざわざ一連のテストをする? • Unit テストは機能結合前のテストなので細かい分岐な どを網羅するのに向いている • UseCase などビジネスロジックを置くところでは,Unit テ ストを補助的に使えるように実装すべき PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 49
  30. PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション 50 どうやって?

    • Facade を使うのはもうやめた • あとは UseCase から Eloquent を剥がすのみ Laravel からロジックを切り離そう!
  31. • Entity を導入 – Eloquent Model の詰め替え先を作る • Repository を導入

    – Eloquent Model,Eloquent Builder に依存する DB アクセスの処理を切り離す 51 UseCase,脱 Eloquent 計画 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  32. Entity • Constructor で属性を受 け取る • Getter で属性を返す 52 実装例

    PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  33. Model • toEntity を実装するこ とで,メソッドチェ ーンで Entity へ変換が 可能に 53

    実装例 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  34. UseCase • 依存するクラスはすべ て Constructor で注入 • User データの取得を Repository

    経由にする 55 実装例 PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  35. • UseCase のロジックから Laravel に依存するも のがなくなった • データの取得部分をモックして,簡単に Unit テストが書けるようになった

    56 Entity & Repository 導入で得られたもの PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション
  36. • Laravel は便利な機能が豊富だけど,Laravel標準の書 き方で推し進めるとつらみが出てくるよ • Facade は依存関係を分かりづらくするよ • Model・Controller だけでは責務の分散がしづらいよ

    • Unit テストを適宜導入することで開発体験が向上する よ • なんちゃってクリーンアーキテクチャ はオススメだよ 58 今回のまとめ PHPerkaigi 2022 04/10 12:45 〜 Track B スポンサーセッション