💡 はじめに
Laravelでサービスクラスを設計する時、
MainService(代表サービス) を挟むべきかどうか、迷ったことはありませんか?
たとえば以下のようなドメインがあるケース:
- 発送(send)
- 入金(payment)
- 契約(contract)
それぞれに一覧・登録・編集などの機能があり、 それをどう整理するかがテーマです。
この記事では、代表サービスを作るべきかどうかに結論を出しつつ、
実際に運用しやすい設計パターンを整理します。
✅ 結論(結論ファースト)
Laravelで MainService 的な代表サービスは原則不要。
サービスは以下のいずれかで十分:
- ドメイン単位で1サービス
- ユースケース(処理)単位で複数サービス
代表サービスが必要になるのは、
複数の処理をまとめて1つの“フロー”として使いたい時のみ。
❓ なぜ代表サービスは不要なのか
1. レイヤーが増えて分かりにくい
代表サービスで List / Store / Update などをラップすると、
依存関係が深くなってわかりにくくなります。
Controller
→ MainService
→ ListService
→ StoreService
→ UpdateService
この構造は「責務を隠すだけ」で、
かえって読み手に不要な階層を強いることになります。
2. 責務が逆流して God クラス化する
MainService に一覧・登録・編集すべてを集約すると、
結局 MainService が責務の塊になりやすいです。
責務の分離という OOP のメリットを
逆に殺してしまうパターンです。
3. 再抽象化になっている
代表サービスは、
ユースケースごとに分割した責務を
再び一箇所でまとめているだけ。
OOP設計の理想である「一意の責務」は失われがちです。
🧱 オススメ設計パターン
🟢 パターン1: ドメイン単位のサービス
各ドメインに1つサービスを持つ構成。
App\Services\Send\SendService
App\Services\Payment\PaymentService
App\Services\Contract\ContractService
💡 サンプルコード
namespace App\Services\Payment;
class PaymentService
{
public function list()
{
return Payment::all();
}
public function store(array $data)
{
return Payment::create($data);
}
public function update(Payment $payment, array $data)
{
$payment->fill($data);
$payment->save();
return $payment;
}
}
コントローラ例
namespace App\Http\Controllers;
class PaymentController extends Controller
{
public function index(PaymentService $service)
{
return $service->list();
}
}
メリット
・依存がシンプル
・読みやすい
・テストしやすい
🟡 パターン2: ユースケース単位で分割する
処理単位で1クラスにする方法。
App\UseCases\Payment\ListPaymentsAction
App\UseCases\Payment\StorePaymentAction
App\UseCases\Payment\UpdatePaymentAction
💡 サンプルコード
namespace App\UseCases\Payment;
class StorePaymentAction
{
public function __invoke(array $data)
{
return Payment::create($data);
}
}
コントローラ例
public function store(StorePaymentAction $action)
{
return $action(request()->validated());
}
メリット
・1クラス1責務で可読性高い
・単体テストしやすい
デメリット
・ファイル数が増える(ただし慣れれば問題なし)
🧩 例外: 代表サービスが必要になるケース
代表サービスが合理的になるのは、
複数ユースケースを1つのフローとしてまとめたい時です。
例:「契約 → 決済 → 発送」を一つの一連処理として実行する場合
この時に作るのは フローサービス であり、単なるラッパーではなく 再利用できるシナリオ になります。
例: App\Flows\OrderFlowService
📌 まとめ
MainService は基本不要
ドメイン単位 or ユースケース単位で設計し、責務を無駄にまとめる抽象化は避ける
(ただし必要な場面でフロー用サービスを作るのはアリ)
設計は状況による部分もありますが、
迷ったらこの順で考えるとスッキリします。
一言
今回ふと保守中のプロジェクトを眺めていて、ごちゃついたサービスクラスを見て、本当はどうあるべきかを調べてみました。
ついでにサクッと文章化したくて、今回初めてAIくんを通してみたのですが、参考書みたいな感じになって個人的には微妙でしたね。
今後は頼らないと思います。