私は普段、Laravelを使用したWebアプリケーション開発をしています。エンジニア歴はもうすぐ1年くらいです。
最近、「どんなデザインパターンを使ったことがある?」と質問されたのですが、恥ずかしながら何も答えられませんでした。デザインパターンについては、優れた設計の見本集のようなものだ、という程度の理解でした。開発において気をつけていることは何点かありますが、それがデザインパターンなのか、どのパターンに当たるのか、といったことを整理したいと思いました。
- サービスコンテナ => Dependency Injectionパターン
- Eloquent => Active Recordパターン
- Manager => Builderパターン
- Middleware => Middlewareパターン
- Facade => Proxyパターン
- Command => Commandパターン?
結論から書くと、Laravelの機能とデザインパターンの対応は上記のようになっているのではないか、と考えています。
間違っている点や不足している箇所がありましたら、教えていただけますと大変ありがたいです。
Laravelの特徴
Laravelが採用しているデザインパターンについて調べるまえに、そもそもPHPフレームワークにおいてLaravelとはどんな位置づけなのかを整理しておきたいと思いました。Laravel以外のフレームワークを触ったことがなかったからです。
Laravelの特徴を検索すると、次のような情報がヒットしました。
- PHPフレームワークの中で最も注目を集めている
- フレームワーク独自の概念などがなく習得しやすい
- 非常に柔軟で多機能であるためWebアプリケーション全般に向いている
- MVCモデルを採用している
- パッケージ管理ツールとしてComposerを使用している
- 日本語の情報が豊富
もう少し詳しく知りたかったので、次にCakePHPと比較している記事を探しました。CakePHPもLaravelと同じく、PHPのWebアプリケーションフレームワークです。
LaravelとCakePHPの比較を調べると、Laravelの特徴は次のように書いてありました。
- ディレクトリ構成などが自由で制約が少ない
- Bladeというテンプレートエンジンが柔軟で使いやすい
- ファイルアップロード処理のインタフェースが用意されている
- バリデーションをコントローラに記述することができる
ディレクトリ構成が自由
CakePHPでは既存のディレクトリ構成を変更することが難しいようですが、Laravelなら自由に変更することができるようです。独自のライブラリクラスを追加する場合も、簡単にそれが実現できるとありました。
Laravelは自由度が高いため、厳密にコーディング規約を定めておかなければ大規模な開発では収集がつかなくなる、とも書いてありました。「Laravelは自由度が大きいため大規模な案件に向く」という意見と「Laravelは厳密に規約を定めないと収集がつかなくなるので小規模案件に向く」という意見があるようでした。
Bladeテンプレートが優秀
BladeはLaravelのテンプレートエンジンです。
テンプレートエンジンとは、データとテンプレートを組み合わせて文字列を作る仕組みのことだそうです。Viewのことだと解釈してもよさそうです。
LaravelのBladeは、Viewに直接PHPを記述することができる点が特徴らしいです。
BladeはシンプルながらパワフルなLaravelのテンプレートエンジンです。他の人気のあるPHPテンプレートエンジンとは異なり、ビューの中にPHPを直接記述することを許しています。全BladeビューはPHPへコンパイルされ、変更があるまでキャッシュされます。つまりアプリケーションのオーバーヘッドは基本的に0です。Bladeビューには
.blade.php
ファイル拡張子を付け、通常はresources/views
ディレクトリの中に設置します。
私はViewにPHPを記述できるのが当たり前だと思っていたので、少し驚きました。
Bladeでは、分岐や繰り返しのための制御構文も充実しているため、複雑なViewも簡単に実現できます。@if
といった記述で制御するのですが、これをディレクティブというようです。
また、Blade内で{{ }}
に囲まれたデータの出力は、自動的にhtmlspecialchars()
が適用されてエスケープされるのですが、他のテンプレートエンジンでは自分でエスケープ処理が必要だったりするようです。
Bladeは、エスケープしたくない場合も次のように書くことで実現できます。
Hello, {!! $name !!}.
バリデーション処理の柔軟性
バリデーションが実行されるレイヤが、Laravelではコントローラ、CakePHPではモデルと異なるようです。
私は普段、Requestクラスにバリデーションルールを定義することが多いです。同じモデルを操作する場合でも、アクションによって許容する入力値を変えたい場合もありますので、CakePHPを触ったことはないので断言できませんが、コントローラ側でバリデーションルールを定義できたほうが便利なんじゃないか、と思っています。
アップロード処理のインタフェース
CakePHPを使う場合は、ファイルアップロードの処理を自分で実装したり、外部のプラグインを使う必要があるらしいです。手間もかかる上に、外部のプラグインを使う場合は設定値などを変更できないケースもあり、柔軟性がないとも書いてありました。
Laravelではファイルストレージ用のインタフェースが最初から用意されているため、数行のコードでファイルアップロード処理が実装できます。保存先をローカルストレージからクラウドストレージに切り替えることも簡単でした。
Laravelが採用するデザインパターン
Dependency Injectionパターン(DIパターン)
Dependency Injection(以下DI)もデザインパターンの一種らしいです。
Laravelにはサービスコンテナと呼ばれる機能が備わっていて、DIが簡単に実現できるようになっています。DIは日本語で依存性の注入と訳されます。クラスが動作するために必要となる別のクラスを、実行時に外から渡してやることで、クラス同士を疎結合にすること、と自分は理解しています。
DIパターンを利用することで、次のようなメリットがあると思います。
- 特定のクラスに依存させないことで、下位モジュールがまだできていなくても、実装を進められる
- 依存するクラスを外部から渡すことができるので、テスト用のクラスに置き換えも容易になる
Active Recordパターン
私は初めて知ったのですが、Active Recordパターンというデザインパターンがあるそうです。
Patterns of Enterprise Application Architectureという書籍で紹介されているそうです。
データベーステーブルまたはビューの行をラップし、データベースアクセスをカプセル化してデータにドメインロジックを追加するオブジェクト
LaravelにはEloquentというORM(Object-relational mapping)がありますが、これがActive Recordの特徴を備えている、という意味の説明を見ました。
ORMはデータベーステーブルのレコードをオブジェクトのように扱うための仕組みだと理解していますが、EloquentではQuery Builderを使ってレコードの集合(コレクション)を操作することができます。この点がActive Recordパターンの特徴と一致しているのだと思いました。
Builderパターン
BuilderパターンはGoFデザインパターンのひとつで、オブジェクト生成の過程を抽象化することが目的です。Directorと呼ばれるクラスで作成過程を定義して、Builderと呼ばれるクラスで表現形式を決定する、と説明されていました。Template Methodパターンでオブジェクトを生成する、という意味だと解釈しました。
LaravelにはQuery Builderというものがありますが、これは他のフレームワークにも登場する一般的な機能のようです。Query Builderは、SQLクエリをORMで実行するためのインタフェース、とあったので、Builderパターンと名前が似ていますが、別物のようです。
一方、Laravelに用意されているIlluminate\Support\Managerクラスは、Builderパターンを実現するための抽象クラスだ、という説明を読みました。
Laravel Managerの例としてAuthorizeManagerを作る
こちらの記事によると、Laravel5.6以降はなぜかLaravel本体ではManagerクラスが使われなくなってきているようですが、Laravelの機能を拡張したい場合は便利みたいです。
Middlewareパターン
LaravelではMiddlewareクラスでアプリケーションに送信されたHTTPリクエストをフィルタリングすることができます。認証や認可に関するチェックをしたり、入力値を整形したりするときに私は使用しています。
Middlewareパターンというデザインパターンにあたるらしいです。
Proxyパターン
LaravelにはFacadeという仕組みがあり、これはサービスクラスに登録したクラスに対する静的なインタフェースを提供する窓口です。
Facadeはサービスコンテナ下で動作するクラスに対してstatic proxyとして動作する、と説明されていたので、デザインパターンにおけるProxyパターンに該当するのではないかと考えています。
Proxyパターンでは、Proxy(代理人)と呼ばれるクラスを通じて目的の処理を呼び出します。このとき、Proxyと実際に処理が定義されているクラスは同じインタフェースを提供するため、呼び出し元はどちらを利用しているか意識せずに処理を実行でき、このことを透過的と表現するようです。
Facadeパターン
GoFデザインパターンのひとつにFacadeパターンというものがありますが、こちらは複数のクラスが関わる複雑な手順をひとつの抽象的なメソッドにまとめ、外部に対してわかりやすいインタフェースを提供することを目的とするらしいので、LaravelのFacadeとは意味が違うようです。
FacadeパターンにおけるFacadeはむしろ、Serviceクラスに近いような印象を受けました。
私が所属しているプロジェクトでは、RepositoryクラスのいくつかのメソッドをServiceクラスのひとつのメソッドとしてまとめ、抽象的なインタフェースを実装することがルールになっています。これにより、Serviceクラスを利用する側は内部の複雑な手順を知る必要がなくなり、同時にControllerとRepositoryが疎結合になるので、Repositoryの修正がしやすくなるなどのメリットがあると思っています。
Commandパターン
Laravelにはコマンドというクラスがあり、ここで定義した処理はコマンドラインから実行することが可能です。
デザインパターンにもCommandデザインパターンというものがあり、こちらはオブジェクトに対する命令をクラスとして定義することで、複雑な操作の組み合わせを簡単に実行できるようにしたり、実行の履歴を残したりすることを目的としているようです。
LaravelのCommandとデザインパターンのCommandは、一連の操作をクラスとして定義するという点では似ていますが、LaravelのCommandはコマンドラインインタフェースを提供することが目的のようなので、少し違うような感じもしています。
Laravelが採用していないデザインパターン
Repositoryパターン
Repositoryパターンは、Laravelは採用していないデザインパターンですが、なぜかLaravelについて検索するとRepositoryパターンに関する記事がたくさんヒットします。それだけRepositoryパターンが便利ということでしょうか。
RepositoryパターンはGoFデザインパターンにはありませんでした。ドメイン駆動設計の中に出てくる考え方のようです。Repositoryパターンの目的は、ビジネスロジックとデータ操作のロジックを分離して、データ操作を抽象化したレイヤに任せることだそうです。
Repositoryを利用する側は、オブジェクトの永続化層が何であるかを意識しなくても良いという利点があり、データベースの移行などの変更にも対応しやすくなります。また、ビジネスロジックとデータ操作を分けてテストすることができるようになります。
まとめ
Laravelと関連するデザインパターン についてまとめましたが、正しいことが書けているか自信がないため、間違いや不足がある箇所がありましたらご指摘いただけるとありがたいです。
Laravelに関連すると思われるデザインパターンは次の通りです。
- サービスコンテナ => Dependency Injectionパターン
- Eloquent => Active Recordパターン
- Manager => Builderパターン
- Middleware => Middlewareパターン
- Facade => Proxyパターン
- Command => Commandパターン?
デザインパターンといえばGoFデザインパターンしか思い浮かびませんでしたが、検索するとそれ以外のデザインパターンがたくさんあることを知れたのが良かったと思います。