Laravelでは、コントローラーにビジネスロジックを直接書くと、保守性・再利用性・テスト性のいずれも損なわれやすくなります。その解決策として有効なのが「サービスクラス」の設計です。
この記事では、Laravelにおけるサービスクラスの設計パターンと、ビジネスロジックの分離によるメリット、テストコードとの連携方法を実例を交えて解説します。
サービスクラスとは?
サービスクラスとは、コントローラーとモデルの間に立って、ビジネスロジックをまとめる役割を担うクラスです。例えば、ユーザー登録処理、決済処理、メール通知など複数の処理を組み合わせるロジックを1つの責任としてまとめます。
従来のコントローラー:
public function store(Request $request)
{
$user = User::create([...]);
Notification::send($user, new WelcomeNotification());
event(new UserRegistered($user));
return redirect()->route('dashboard');
}
このように複数の処理が混在する場合、テストしづらく、ロジックの流用も困難になります。
サービスクラスの基本構造
Laravelでは、以下のようなディレクトリ構成でサービスクラスを配置するのが一般的です。
app/
├── Services/
│ └── UserService.php
サービスクラスの実装例:
namespace App\Services;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Notification;
class UserService
{
public function register(array $data): User
{
$user = User::create($data);
Notification::send($user, new WelcomeNotification());
event(new \App\Events\UserRegistered($user));
return $user;
}
}
コントローラーからの呼び出し
コントローラーでは、責務を限定し、サービスクラスに処理を委譲します。
use App\Services\UserService;
class RegisterController extends Controller
{
public function __construct(protected UserService $userService) {}
public function store(Request $request)
{
$user = $this->userService->register($request->validated());
return redirect()->route('dashboard');
}
}
__construct()
で依存注入(DI)すれば、サービスクラスのテストやMock差し替えも容易になります。
サービスクラス設計のメリット
- 関心の分離:コントローラーはルーティングとレスポンスのみに集中
- 再利用性:同じロジックをAPI・バッチ・CLIなどでも流用可能
- テスト性:サービス単位でユニットテストを書きやすくなる
サービスクラスに依存するテストの書き方
サービスクラスにメソッドを持たせることで、ユニットテストを明確に定義できます。
public function test_user_registration()
{
$service = new UserService();
$user = $service->register([
'name' => 'テストユーザー',
'email' => 'test@example.com',
'password' => bcrypt('secret'),
]);
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);
}
ビジネスルールをサービス単位でテストできることで、LaravelのFeatureTestよりも高速かつ安定したテストを構築できます。
よくあるアンチパターンと改善ポイント
サービスクラスにすべてを詰め込みすぎると、巨大な「神クラス」になりやすくなります。以下のような設計に注意しましょう。
- DB処理・通知処理・バリデーションが混在 → 処理単位でメソッド分割
- 1クラスで10以上のpublicメソッド → サブサービスやアクションクラスへの委譲を検討
サービスクラスはあくまで「中間制御役」であり、実装は可能な限り小さく保つことが推奨されます。
まとめ
Laravelにおけるサービスクラスは、ビジネスロジックを整理し、保守性・テスト性・再利用性を高める設計の核です。
- 複雑なロジックをコントローラーから分離
- 依存注入でテスト可能な構造を実現
- ルールに応じてクラス分割・責務整理を行う
規模が大きくなるほど、ロジックの所在が明確な設計が求められます。サービスクラスを上手に使い、Laravelプロジェクトを長期的に運用しやすい構造にしていきましょう。