Laravel Service Repository Action Pattern Thin Controller
Definisi
Tiga pola pemisahan tanggung jawab di Laravel untuk menjaga controller tetap tipis:
- Service: kelas yang berisi logika bisnis inti, dipanggil dari controller/action.
- Repository: abstraksi akses data (query Eloquent) supaya controller tidak bergantung langsung ke model/query.
- Action (Single Action Class): kelas dengan satu method
handle()yang melakukan satu tugas spesifik.
Kapan dipakai
- Service: logika yang melibatkan beberapa model atau aturan bisnis, dipakai di lebih dari satu tempat.
- Repository: kalau kamu ingin mengganti sumber data (DB → API) tanpa menyentuh controller, atau mau test dengan mock.
- Action: satu operasi spesifik yang punya nama jelas (misal
SendInvoiceAction), supaya controller hanya memanggil satu kelas.
Jangan over-engineer: untuk CRUD sederhana, langsung query di controller/model sudah cukup.
Cara kerja
Service
class OrderService
{
public function __construct(private PaymentGateway $gateway) {}
public function checkout(Cart $cart): Order
{
$order = Order::create([...]);
$this->gateway->charge($cart->total());
return $order;
}
}Dipakai di controller:
public function store(OrderService $svc)
{
$svc->checkout($cart);
return redirect()->route('orders.index');
}Repository
interface UserRepository
{
public function findByEmail(string $email): ?User;
}
class EloquentUserRepository implements UserRepository
{
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
}Daftarkan binding di AppServiceProvider:
$this->app->bind(UserRepository::class, EloquentUserRepository::class);Action
class SendWelcomeEmail
{
public function handle(User $user): void
{
Mail::to($user->email)->send(new Welcome());
}
}Struktur alur
Loading diagram...
Contoh kombinasi
public function register(RegisterRequest $req, RegisterUserAction $action)
{
$action->handle($req->validated());
return redirect('/dashboard');
}RegisterUserAction bisa memanggil UserRepository dan OrderService di dalamnya.
Catatan implementasi
- Inject dependency lewat constructor; jangan
newdi dalam method (sulit di-test). - Repository sebaiknya kembalikan domain object, bukan query builder mentah.
- Gunakan Action saat nama operasi lebih jelas dari method service umum.
Kesalahan umum
- Controller gemuk: semua logika di controller, sulit di-test dan dirawat.
- Repository jadi query duplikat: cuma memindahkan
User::wheretanpa nilai abstraksi. - Over-abstraksi: membuat service/repo/action untuk operasi sebaris.
- New di dalam class: dependency hardcoded, tidak bisa di-mock saat test.