第37回: イベントとリスナ — 処理を「発火側と受け取り側」に分けて疎結合にする

章: 第4章: Laravelフレームワーク

新機能を追加するたびに、既存のコントローラやサービスを書き換えていませんか?

「ユーザー登録後にウェルカムメール送信・ポイント付与・Slack通知をしたい」という要件が増えるたびに同じコントローラを修正するのは、変更のリスクと範囲が広がります。イベントとリスナを使えば、既存コードを触らずに新しい動作を追加できます。

イベント・リスナなしで機能を追加し続けると何が起きるか

コントローラがユーザー登録後の「その後の処理」をすべて知っている状態になり、変更のたびに影響範囲が広がります。イベントを発火するだけにすることで、コントローラは何が起きるかを関知しなくて済みます。

イベント設計の良い例・悪い例

観点 悪い例 良い例
後処理の追加 コントローラに直接処理を追加 新しいリスナクラスを作って登録するだけ
関心の分離 1つのアクションが複数の後処理を把握 イベントを発火するだけでコントローラが完結
非同期化 全処理をリクエスト内で実行 リスナに ShouldQueue を実装してバックグラウンド化
テスト 全処理を通して動作確認 Event::fake() で発火確認だけに絞れる

チェックポイント: Event::fake() をテストで使うと、実際のリスナを動かさずにイベントが発火されたかだけを確認できます。逆に統合テストではリスナまで通して動作確認しましょう。どちらのテストが必要かを意識して使い分けてください。

コードサンプル


<?php
// php artisan make:event UserRegistered
class UserRegistered {
    public function __construct(public User $user) {}
}
// php artisan make:listener SendWelcomeNotification
class SendWelcomeNotification {
    public function handle(UserRegistered $event): void {
        Notification::send($event->user, new WelcomeNotification());
    }
}
// イベント発火
event(new UserRegistered($user));

まとめ & 次のステップ

  • イベントとリスナで発火側と受け取り側を分けると、既存コードを変えずに動作を追加できます
  • Event::fake() でテスト中にイベントの発火を確認できます
  • リスナに ShouldQueue を実装すると非同期で実行できます
  • EventServiceProvider でイベントとリスナの対応を登録しましょう
  • 1つのイベントに複数のリスナを紐付けることで、後から機能を増やせます

次回は Artisanコマンド作成 を学びます。繰り返し作業をコマンド1本で実行できる仕組みを整理しましょう。

Related Articles