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

Laravel × レイヤードアーキテクチャを実践して得られた知見と反省 / Practice of Laravel with layered architecture

Shohei Okada
December 15, 2018

Laravel × レイヤードアーキテクチャを実践して得られた知見と反省 / Practice of Laravel with layered architecture

DDD においてセットで語られるレイヤードアーキテクチャ。このたび 2 つのプロジェクトにおいて、Laravel 上でレイヤードアーキテクチャを実践する機会に恵まれたので、Laravel の機能・特徴にも焦点をあてながら、事例とともに得られた知見・反省などをお話します。

2018/12/15 開催の PHP Conference 2018 (http://phpcon.php.gr.jp/2018/) の発表資料です。

youtube: https://youtu.be/2D8Rs9SqFiU?t=13762
joind.in: https://joind.in/event/japan-php-conference-2018/laravel--

Shohei Okada

December 15, 2018
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. Laravel
    ×
    レイヤードアーキテクチャ

    実践して得られた
    知見と
    反省
    Japan PHP Conference 2018
    @okashoi WILLGATE, Inc.

    View Slide

  2. 岡田 正平(おかだ しょうへい)@okashoi
    • 株式会社ウィルゲート
    • PHP, Laravel, Vue.js
    • Golang はじめました
    2
    自己紹介
    Slides:

    View Slide

  3. Services Tech Blog Events
    ブログやってます!イベントやってます!
    「ウィルゲート テックブログ」で検索!

    View Slide

  4. 4
    今年の phpcon のテーマ = “GROWTH”

    View Slide

  5. 今日の目標:私たちの失敗を共有すること
    • 人や組織は「失敗」によって成長する
    • 成功には再現性はないが、失敗には再現性がある
    ➢ 私が携わった 2 つのプロジェクトについて
    具体的な事例、特に失敗を共有し
    皆さんの糧にしてもらいたい
    5
    今年の phpcon のテーマ = “GROWTH”

    View Slide

  6. 今回は「具体的にどのような実装になるか」
    という話(※)はほとんど出てきません
    ※またどこか別の機会を得て話したい

    View Slide

  7. • 背景
    • Laravel とレイヤードアーキテクチャの親和性
    • 失敗したこと
    • おわりに
    7
    目次

    View Slide

  8. • 背景
    • Laravel とレイヤードアーキテクチャの親和性
    • 失敗したこと
    • おわりに
    8
    目次

    View Slide

  9. プロジェクト A
    • 2018 年 4 月~
    • Laravel で HTML を render して返す
    典型的な web アプリケーション
    • PL + 実装者 2 名 +
    プロジェクト B
    • 2018 年 6 月~
    • SPA(API を Laravel で実装)
    • PM + フロント 3 名 + サーバサイド 4 名 +
    9
    2 つのプロジェクト

    View Slide

  10. • いずれも実装者のほとんどが Laravel 未経験
    • なぜ Laravel?
    • 技術のキャッチアップのため
    • チーム横断人材の育成のため
    • の役割はアプリケーションアーキテクト
    • 別チームで Laravel をメインで使っていたので
    白羽の矢が立った
    10
    2 つのプロジェクト

    View Slide

  11. ※ 2018 年 3 月ごろ
    「チームはわざわざ『Laravel に切り替える』
    というコストを払っている……」
    11
    自分が提供できる価値を考える

    View Slide

  12. ※ 2018 年 3 月ごろ
    「Laravel をただの MVC フレームワークとして
    使うのでは得られるものが少ないのでは?」
    12
    自分が提供できる価値を考える

    View Slide

  13. かくして、チームを巻き込んだ
    レイヤードアーキテクチャへの
    試行錯誤の旅が始まった

    View Slide

  14. • 背景
    • Laravel とレイヤードアーキテクチャの親和性
    • 失敗したこと
    • おわりに
    14
    目次

    View Slide

  15. • ディレクトリ構成を自由に設定できる
    • DI のための仕組みが充実
    • Eloquent ORM などの豊富で強力な機能
    15
    Laravel の特徴

    View Slide

  16. • ディレクトリ構成を自由に設定できる
    • DI のための仕組みが充実
    • Eloquent ORM などの豊富で強力な機能
    16
    Laravel の特徴
    →レイヤードアーキテクチャと相性がいい
    →どこまでその機能を活用するか悩ましい

    View Slide

  17. • もとから Laravel に存在している
    概念は該当する層の中に移動
    • 依存性の逆転を発生させる際に
    DI の仕組みを活用
    17
    自由なディレクトリ構成と DI の仕組み

    View Slide

  18. • もとから Laravel に存在している
    概念は該当する層の中に移動
    • 依存性の逆転を発生させる際に
    DI の仕組みを活用
    18
    自由なディレクトリ構成と DI の仕組み
    Application
    Infrastructure
    Domain
    処理の流れ 依存の方向

    View Slide

  19. 出典: DDDパターンを活用したLaravelアプリケーション開発
    /ddd-with-laravel
    https://speakerdeck.com/shin1x1/ddd-with-laravel
    (めちゃくちゃ参考にしました 感謝 )
    19
    参考にさせていただいたもの

    View Slide

  20. • 背景
    • Laravel とレイヤードアーキテクチャの親和性
    • 失敗したこと
    • おわりに
    20
    目次

    View Slide

  21. 失敗 1
    Value Object
    が形骸化した

    View Slide

  22. 本来の意図
    値の振る舞いや制約をオブジェクトで表現する
    • 例)「価格」 Value Object
    • マイナス値を取らない
    • 消費税込みの金額を算出できる など
    実際に起きたこと
    全カラムと 1 対 1 のクラスを作ることが目的に
    ➢ ロジックの無いクラスが大量生産される
    22
    Value Object の形骸化

    View Slide

  23. namespace App¥Domain¥Models¥News;
    /**
    * Class Title
    */
    class Title
    {
    /**
    * @var string
    */
    private $value;
    /**
    * @param string $value
    */
    public function __construct(string $value)
    {
    $this->value = $value;
    }
    /**
    * @return string
    */
    public function value(): string
    {
    return $this->value;
    }
    }
    このようなクラスが
    DB のカラムの数だけ
    作成された

    View Slide

  24. どうすればよかったか?
    • Value Object に「どんなメリットがあるか」
    をベースにしてメンバーの認識すり合わせる
    • 振る舞いや制約がない値は Value Object にしない
    • 自由入力形式のテキストや bool 値など
    24
    Value Object の形骸化

    View Slide

  25. 失敗 2
    Domain 層に
    整形処理を書いた

    View Slide

  26. json API の実装において Entity や Value Object に
    を実装
    26
    整形処理の実装箇所
    class User implements ¥JsonSerializable
    {
    // 略
    public function jsonSerialize(): array
    {
    return [
    'id' => $this->id,
    'mail_address' => $this->mailAddress,
    'profile' => [
    'last_name' => $this->lastName,
    'first_name' => $this->firstName,
    'user_name' => $this->userName,
    'sex' => $this->sex,
    ],
    ];
    }
    }
    {
    "id": 1,
    "mail_address": "[email protected]",
    "profile": {
    "last_name": "Doe",
    "first_name": "John",
    "user_name": "johndoe",
    "sex": "male"
    }
    }
    jsonSerialize()

    View Slide

  27. Controller が薄く、はじめは良い実装に見えた
    27
    整形処理の実装箇所
    class UserController extends Controller
    {
    public function detail(User $user)
    {
    return response(['user' => $user]);
    }
    public function create(CreateRequest $request, UserRepository $userRepository)
    {
    $user = $userRepository->create($request->validated());
    return response(['user' => $user], 201);
    }
    public function update(User $user, UpdateRequest $request, UserRepository $userRepository)
    {
    $user = $userRepository->update($user->id(), $request->validated());
    return response(['user' => $user], 200);
    }
    }

    View Slide

  28. • 一方で「ユーザ一覧 API」では
    次のようなレスポンス欲しくなった
    • 対応するために Entity に という
    プロパティをもたせて条件分岐させるようにした
    ➢ ドメインの関心ごと以外が混ざってしまった
    28
    整形処理の実装箇所
    {
    "users": [
    {"id": 1, "user_name": "jonedoe"},
    {"id": 2, "user_name": "janedoe"},
    // ...
    ]
    }
    $requestType

    View Slide

  29. どうすればよかったか? その 1
    • そもそも整形処理(レスポンス形式)自体が
    ドメインの関心ごとではない
    ➢ Application 層に整形を担う
    Presenter のようなクラスを設置する
    • 「Controller を薄くすること」を目的にしない
    • あくまで 1 つの指標に過ぎない
    29
    整形処理の実装箇所

    View Slide

  30. どうすればよかったか? その 2
    • CQRS を適用する
    • 副作用のある Command と
    副作用のない Query で役割を分割する
    ➢ 「一覧取得」は Query で行い
    Entity を経由せずに、連想配列の形で扱ってしまう
    30
    整形処理の実装箇所

    View Slide

  31. 失敗? 3
    作るシステムの
    規模を考慮しなかった

    View Slide

  32. プロジェクト A の場合
    • 最初のリリースまでに 3 ヶ月程度
    • リリース後に大きな追加開発はしない想定
    • 一般に公開はしないクローズドなシステム
    ➢ システムの規模に対してクラスの種類が多く
    機能実装のオーバーヘッドが大きくなっていたの
    ではないか?
    32
    規模に見合ったアーキテクチャか?

    View Slide

  33. 33
    規模に見合ったアーキテクチャか?
    MVC レイヤードアーキテクチャ
    • routing
    • Controller (Request)
    • Model (ORM)
    • View
    • routing
    • Controller (Request)
    • Use Case
    • Entity
    • Value Object
    • Domain Service
    • Repository (Interface)
    • Repository (Implementation)
    • ORM
    • View
    MVC と比較して考慮するべき概念は増える

    View Slide

  34. レイヤードアーキテクチャのメリット(私的解釈)
    • MVC をもう少し詳細に分割・責務を明確化
    (”Model” が指すものは人によってまちまち)
    ➢ コードの可読性が保たれる中長期的なメリット
    • 「依存性の逆転」を活用して
    ドメイン知識を何にも依存させない(保護する)
    ➢ 複雑な問題を扱うシステムほどその効果が大きい
    34
    規模に見合ったアーキテクチャか?

    View Slide

  35. どうすればよかったか?
    • 複雑でないシステムなら MVC でも充分
    • レイヤードアーキテクチャに出てくる概念の
    一部だけを適用するのも良い
    • 一方で、この挑戦によって得られた知見が
    たくさんあった事実も軽視できない
    ➢ これらを踏まえて、ステークホルダーの合意を
    取ったうえで採用するのならば問題はない
    (今回は合意を取っていた)
    35
    規模に見合ったアーキテクチャか?

    View Slide

  36. 失敗? 4
    ドメインモデリングと
    設計が分かれていた

    View Slide

  37. • いずれも実装者のほとんどが Laravel 未経験
    • なぜ Laravel?
    • 技術のキャッチアップのため
    • チーム横断人材の育成のため
    • の役割はアプリケーションアーキテクト
    • 別チームで Laravel をメインで使っていたので
    白羽の矢が立った
    37
    再掲:2 つのプロジェクト

    View Slide

  38. • 別チームからの兼任なので
    サービスや業務フローへの理解が浅い状態
    • アプリケーションアーキテクトとしての参加で
    要件定義には参加していない
    • 一度要件に落とされたものをベースに設計
    ➢ 伝言ゲーム形式になって
    本質を突いた設計になっているかがわからない
    38
    モデリングと設計の分離

    View Slide

  39. どうすればよかったか?
    • レイヤードアーキテクチャの恩恵を
    存分に受けたければドメイン知識の理解は必須
    • 理解が深い者が設計を行う
    • 設計者が理解を深める
    • ただし、実装パターンとして適用するだけでも
    受けられる恩恵はあるので無意味とはいえない
    • テストコードが書きやすい
    • プルリクの粒度が小さくなった? など
    39
    モデリングと設計の分離

    View Slide

  40. • 背景
    • Laravel とレイヤードアーキテクチャの親和性
    • 失敗したこと
    • おわりに
    40
    目次

    View Slide

  41. レイヤードアーキテクチャの採用は正解だったのか?
    41
    おわりに

    View Slide

  42. レイヤードアーキテクチャの採用は正解だったのか?
    (現時点では)正直わからない......
    ➢ その設計が「良かった」かどうかは
    数年後になって初めて判断できるようになる
    42
    おわりに

    View Slide

  43. 「設計として」正解かどうかはわからない
    だけど
    取り組んだことそれ自体は
    正解にすることができる
    (やったからには、何が何でも正解にしたい)
    43
    おわりに

    View Slide

  44. • メンバーに Laravel の知識や
    レイヤードアーキテクチャの概念を伝搬する
    • 試行錯誤の過程で生まれたドキュメント等を
    組織の資産にする
    • (失敗)事例は外に向けてアウトプットする
    ➢ 自分/組織/社会の成長の糧にする
    44
    やったからには余すこと無く価値に変える

    View Slide

  45. • メンバーに Laravel の知識や
    レイヤードアーキテクチャの概念を伝搬する
    • 試行錯誤の過程で生まれたドキュメント等を
    組織の資産にする
    • (失敗)事例は外に向けてアウトプットする
    ➢ 自分/組織/社会の成長の糧にする
    45
    やったからには余すこと無く価値に変える
    今日、この場でお話ししているのも
    「取り組んだことそれ自体」を正解にするため

    View Slide

  46. 46
    さて、今年の phpcon のテーマは?

    View Slide

  47. 47
    さて、今年の phpcon のテーマは?

    View Slide

  48. 挑戦して
    その結果を発信して
    みんなで”成長”しましょう
    来年の phpcon で
    お待ちしています!

    View Slide