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. プロジェクト A • 2018 年 4 月~ • Laravel で

    HTML を render して返す 典型的な web アプリケーション • PL + 実装者 2 名 + プロジェクト B • 2018 年 6 月~ • SPA(API を Laravel で実装) • PM + フロント 3 名 + サーバサイド 4 名 + 9 2 つのプロジェクト
  2. • いずれも実装者のほとんどが Laravel 未経験 • なぜ Laravel? • 技術のキャッチアップのため •

    チーム横断人材の育成のため • の役割はアプリケーションアーキテクト • 別チームで Laravel をメインで使っていたので 白羽の矢が立った 10 2 つのプロジェクト
  3. • ディレクトリ構成を自由に設定できる • DI のための仕組みが充実 • Eloquent ORM などの豊富で強力な機能 16

    Laravel の特徴 →レイヤードアーキテクチャと相性がいい →どこまでその機能を活用するか悩ましい
  4. • もとから Laravel に存在している 概念は該当する層の中に移動 • 依存性の逆転を発生させる際に DI の仕組みを活用 18

    自由なディレクトリ構成と DI の仕組み Application Infrastructure Domain 処理の流れ 依存の方向
  5. 本来の意図 値の振る舞いや制約をオブジェクトで表現する • 例)「価格」 Value Object • マイナス値を取らない • 消費税込みの金額を算出できる

    など 実際に起きたこと 全カラムと 1 対 1 のクラスを作ることが目的に ➢ ロジックの無いクラスが大量生産される 22 Value Object の形骸化
  6. <?php 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 のカラムの数だけ 作成された
  7. 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()
  8. 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); } }
  9. • 一方で「ユーザ一覧 API」では 次のようなレスポンス欲しくなった • 対応するために Entity に という プロパティをもたせて条件分岐させるようにした

    ➢ ドメインの関心ごと以外が混ざってしまった 28 整形処理の実装箇所 { "users": [ {"id": 1, "user_name": "jonedoe"}, {"id": 2, "user_name": "janedoe"}, // ... ] } $requestType
  10. どうすればよかったか? その 1 • そもそも整形処理(レスポンス形式)自体が ドメインの関心ごとではない ➢ Application 層に整形を担う Presenter

    のようなクラスを設置する • 「Controller を薄くすること」を目的にしない • あくまで 1 つの指標に過ぎない 29 整形処理の実装箇所
  11. どうすればよかったか? その 2 • CQRS を適用する • 副作用のある Command と

    副作用のない Query で役割を分割する ➢ 「一覧取得」は Query で行い Entity を経由せずに、連想配列の形で扱ってしまう 30 整形処理の実装箇所
  12. プロジェクト A の場合 • 最初のリリースまでに 3 ヶ月程度 • リリース後に大きな追加開発はしない想定 •

    一般に公開はしないクローズドなシステム ➢ システムの規模に対してクラスの種類が多く 機能実装のオーバーヘッドが大きくなっていたの ではないか? 32 規模に見合ったアーキテクチャか?
  13. 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 と比較して考慮するべき概念は増える
  14. どうすればよかったか? • 複雑でないシステムなら MVC でも充分 • レイヤードアーキテクチャに出てくる概念の 一部だけを適用するのも良い • 一方で、この挑戦によって得られた知見が

    たくさんあった事実も軽視できない ➢ これらを踏まえて、ステークホルダーの合意を 取ったうえで採用するのならば問題はない (今回は合意を取っていた) 35 規模に見合ったアーキテクチャか?
  15. • いずれも実装者のほとんどが Laravel 未経験 • なぜ Laravel? • 技術のキャッチアップのため •

    チーム横断人材の育成のため • の役割はアプリケーションアーキテクト • 別チームで Laravel をメインで使っていたので 白羽の矢が立った 37 再掲:2 つのプロジェクト
  16. どうすればよかったか? • レイヤードアーキテクチャの恩恵を 存分に受けたければドメイン知識の理解は必須 • 理解が深い者が設計を行う • 設計者が理解を深める • ただし、実装パターンとして適用するだけでも

    受けられる恩恵はあるので無意味とはいえない • テストコードが書きやすい • プルリクの粒度が小さくなった? など 39 モデリングと設計の分離
  17. • メンバーに Laravel の知識や レイヤードアーキテクチャの概念を伝搬する • 試行錯誤の過程で生まれたドキュメント等を 組織の資産にする • (失敗)事例は外に向けてアウトプットする

    ➢ 自分/組織/社会の成長の糧にする 45 やったからには余すこと無く価値に変える 今日、この場でお話ししているのも 「取り組んだことそれ自体」を正解にするため