Help us understand the problem. What is going on with this article?

Laravel のモデルクラスをどこに配置するか問題について考えてみる

この記事について

Laravel Advent Calender 2019 2日目の記事です。

Laravel では、モデルクラスの置き場所が決められておらず、デフォルトで作成される User クラスは app 直下に置かれています。とはいえ、app 直下にすべてのモデルクラスを置いてしまうと、ツリービューで見たときの視認性が悪くなってしまうので、できれば役割やコンテキストごとに分割して配置したい、という気持ちになります。

これまで10近く Laravel を使ったアプリケーションに携わってきて、様々な構成を見てきましたが、わりと最近はひとつの形に収斂されてきてる印象を受けるので、問題提起を兼ねて、様々なパターンについてメリット/デメリットを考察しつつ、どういう配置がいいのか探ってみようという試みです。

----- 2019-10-08 12:36 追記 -----
後述する「4. アプリケーションと独立した Domain」の具体的な構成・メリット・デメリットについて、 @kazuhei さんがこちらの記事で言及してくださいました、合わせてお読みください。

Laravelで作るアプリケーションとドメインを(ついでにinfraも)独立させたパターン - Qiita

----- 追記ここまで -----

はじめに

環境

  • Laravel 6.6.0

Model/モデルの定義

本記事では「Model」(アルファベット表記のもの)は Eloquent Model を指し、「モデル」(カタカナ表記のもの)は概念的なものを指します。本記事ではビューとコントローラー以外はすべてモデルとして扱います。

パターン一覧

  1. デフォルト
  2. Models
  3. Entities, ValueObjects, Services, etc
  4. アプリケーションと独立した Domain

初期構成

必要になったらディレクトリができるので、初期状態はすっきりしています。

$ tree -L 1 app
app
├── Console
├── Exceptions
├── Http
├── Providers
└── User.php

これに、Events, Notifications, Policies といった標準で規定されたディレクトリが加わります(これらもカスタマイズは可能ですが、特別な理由がなければそのまま使うほうがいいでしょう。

パターン1: デフォルト

前述の通り、app 直下に配置するパターンです。

配置例

tree -L 1 app         
app
├── Console
├── Customer.php
├── Deliverer.php
├── Exceptions
├── Http
├── Order.php
├── Providers
└── User.php

メリット

php artisan make:model Hoge と実行すると app/Hoge.php ができます。最少のタイプ数で作成できるのがメリットです。

デメリット

こちらも前述の通り、ツリービューで見たときにずらずらと Model のファイルが並んでしまうので、視認性が悪くなります。

所感

中には、ツリービューは見ないあるいは視認性の悪さは気にならない、という方もいるかもしれませんが、私は無理だったので、数個程度のファイルでアプリケーションが構成されているのでなければ、こちらのパターンは選択しないでしょう。

パターン2: Models

app/Models 以下に配置するパターンです。

Laravel4 の時代には models ディレクトリがあったんですが、5 になってなくなりました。4時代から触っていて、それに慣れていたので、なくなったときは、えーなんでなくしたの?と思いました。

配置例

$ tree -L 2 -d app
app
├── Console
├── Exceptions
├── Http
│   ├── Controllers
│   └── Middleware
├── Models
│   ├── Base
│   ├── Delivery
│   └── Order
└── Providers

メリット

パターン3 との対比になりますが、このパターンだと、役割ごとではなくコンテキストあるいは集約ルートごとの分割が容易になります。

注文と配送というコンテキストがあるとして、Models 以下のようにコンテキストごとに分割して配置することができます。さらにコンテキストごとに Entities, Services などをつくってもいいでしょう。

デメリット

こちらもパターン3 との対比になりますが、コンテキストがひとつないしはそれほど多くなく、コンテキストごとに振る舞いが変わらないようなドメインの場合は、階層が増えるだけであまり意味がなくなってしまうかもしれません。

所感

いまのところこれがいちばんしっくりきています。Policy や Observer のような Model に密接に関わるクラスをどこに配置するか(デフォルトか Models 以下か)というのは悩ましいところではあるんですが、いまのところはデフォルトがいいのかな、と感じています。

パターン3: Entities, ValueObjects, Services, etc

app/Entities, app/Services など、モデルの種類ごとにディレクトリを切って配置するパターンです。最近はわりとこのパターンに遭遇することが多いです(書籍やインターネット上のリソースで推奨しているものがあるんでしょうか)。

配置例

$ tree -L 2 -d app                                                                     
app
├── Console
├── Entities
│   ├── Deliver
│   └── Order
├── Exceptions
├── Http
│   ├── Controllers
│   └── Middleware
├── Providers
├── Services
│   ├── Deliver
│   └── Order
└── ValueObjects
    ├── Deliver
    └── Order

メリット

パターン2 との対比になりますが、コンテキストごとに分けたいのであれば、種類ごとのディレクトリの下で分割する形になります。その結果、種類ごとのディレクトリの下にそれぞれディレクトリができることになるので、コンテキストがひとつあるいはごく少なければ、いちばん簡潔な構成かもしれません。

デメリット

普段これでやっててあまりデメリットは感じてないですが、強いて挙げるとすれば、クラスの種類に引きずられて、関連の強いクラスが分断されてしまう恐れがあるとか、実態は値オブジェクトでないのに ValueObjects の中にあって混乱する、とかでしょうか。

所感

パターン2を選ぶか3を選ぶか、というのは、極論で言えば好みの問題、ということになる気はします。個人的には、モデルの種類(Entity なのか Service なのか)というのはあまり気にならなくて、どちらかといえば、どのコンテキストのクラス(オブジェクト)なのか、のほうに意識があるので、パターン2 を推しますが、チームでよく話し合って決めればいいのかな、と思います。

パターン4: アプリケーションと独立した Domain

最近また盛り上がりを感じるドメイン駆動設計的な、クリーンアーキテクチャ的な、フレームワークへの依存性をゼロにする、あるいは極力小さくする、という戦略のもとにつくられる、ディレクトリ構成です。

私はこのような方針で Laravel を採用しているプロジェクトには関わったことがなく、細かいメリット・デメリットは想像の範囲内でしかわからないので、配置例のみ記載することにします。もし実際に採用されている方がいれば、コメントにてメリット・デメリットを教えていただけるとありがたいです。

実装の詳細はこちらの記事を参考にするといいかもしれません。

独立したコアレイヤパターン - Shin x Blog

配置例

$ tree -L 2 -d ./app ./domain
./app
├── Console
├── Exceptions
├── Http
│   ├── Controllers
│   └── Middleware
├── Infrastructure
│   └── Repositories
└── Providers
./domain
├── Delivery
│   ├── Entities
│   └── Repositories
└── Order
    ├── Entities
    └── Repositories

上記例では、リポジトリパターンを導入し、domain/{Context}/Repositories 配下には interface を、app/Infrastructure/Repositories 配下には実装クラスをそれぞれ配置します。原則的に domain 以下は POPO (Plain Old PHP Object) なクラスになるので、フレームワークに対して疎結合にできるメリットがあります。

あと、「Policy や Observer のような Model に密接に関わるクラス」との関係をどうするか、という問題があって、これは、

  1. 使わないで自前で仕組みを用意する
  2. 使うが中身はドメインモデルに委譲できるようにする

といった解決策がありそうです。そこら辺も事前に決めておく必要があるでしょう。

おわりに

個人的にはパターン2 のように app/Models 以下にすべてを配置する形を推したいですが、パターン3 のようにクラスの種類ごとにディレクトリを切る形でもいまのところそれほど不満はありません。

上記以外のメリット・デメリットを感じている方、あるいは上記以外で、ウチではこんな構成でやってます、というのがあれば、メリット・デメリット合わせて教えていただけると助かります :bow:

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away