✨
【Laravel】Service層を使用しControllerの肥大化を防止する
はじめに
Service層に切り分け、Controllerの処理を簡潔にする方法について
今まで学んだことを備忘録として残します。
Service層に切り分けるメリット
-
可読性が高くなる
- ControllerはHTTPリクエストを受けて、最終的にHTMLもしくはJSONなどを返すことが本来の目的
-
再利用しやすくなる
- 一度書いた処理を他の場所でも使えるようになる
-
単体でテストを行いやすくなる
- 一つ一つの機能(メソッド)を独立してテストできる
-
ソースコードを調査しやすい
- 責任が明確に分かれるので、どこを探せばいいかがわかりやすい
など多くのメリットがあります。
ControllerとService層の切り分け例
ビフォー
簡易的ですが、以下は記事登録でControllerに処理を集めた時のビフォー例です。
ArticleController
public function store(StoreArticleRequest $request)
{
try {
$article = new Article();
$article->fill([
'title' => $request->title,
'body' => $request->body,
'start_date' => $request->start_date,
])->save();
return response()->json(['success' => '登録に成功しました。']);
} catch (Exception $e) {
return response()->json(['error' => '登録に失敗しました。'], 400);
}
}
アフター
以下はService層に切り分けた時のアフター例です。
このように切り分けると、各々の処理の責任を分けることができ、
Controllerをよりシンプルにすることができます。
ArticleController
public function store(StoreArticleRequest $request, CreateArticleService $service)
{
try {
$article = $service->handle($request);
return response()->json(['success' => '登録に成功しました。']);
} catch (Exception $e) {
return response()->json(['error' => '登録に失敗しました。'], 400);
}
}
App\Services\CreateArticleService.php
<?php
namespace App\Services;
// 一部省略
class CreateArticleService
{
public function handle(Request $request)
{
try {
$article = new Article();
$article->fill([
'title' => $request->title,
'body' => $request->body,
'start_date' => $request->start_date,
])->save();
return $article;
} catch (Exception $e) {
throw new Exception('登録に失敗しました。');
}
}
}
今回の「ビフォー」では、既にLaravelの標準機能であるFormRequest
で
バリデーションを行なっており、適切に切り分けられています。
また、Middleware
によるリクエストの前後処理、Policies
による認可処理など、
標準機能を使用することで、Controllerの肥大化をより防止することができます。
補足
それぞれのModelごとにServiceクラスを作成する、または各Modelに直接登録処理などを
記述する方法は、あまり効率的ではない場合が多いです。
これは、主に以下の理由のためです。
- 一つの処理で複数のモデルを触る必要がある場合に、どのモデルに処理を記述すればいいかが不明確でわからない
- メソッドがたくさん追加され、可読性が悪くなる
- ソースコードを追う場合にどこを見ればいいかわかりづらい
そのため、ユーザー側から見たときの処理の内容(記事の登録、記事の更新など)を基準に
Serviceクラスを作成し処理を整理すると、
どこに何の処理を記述すればいいかが明確で、コードも追いやすくなります。
これにより、基本的にはユーザー中心の基準に従うと、
コードの整理・管理がしやすくなります。
さいごに
今回の例はシンプルなものでしたが、Controllerが肥大化しやすいのは
よくあることだと思います。
まだ改善できることは多くあると思いますが、
責任が明確に分かれることで可読性が高く、テストも行いやすくなるので、
積極的にService層への切り分けを行っていきたいと思います。
Discussion