Laravel Repository Pattern (Hopefully) the Right Way

Akbar M. Adhatama
Javan Cipta Solusi
Published in
3 min readApr 24, 2017
Laravel Repository Pattern

Sudah beberapa kali aku mengerjakan projek Laravel menggunakan Repository Pattern.
Kami biasa pake ini https://github.com/andersao/l5-repository.

Yang menggangguku adalah, Repository Pattern jadi semacam layer untuk melakukan business logic daripada untuk membuat abstraksi pada interaksi data objek.

Tentang Repository Pattern

Sebelum itu, kita perlu tahu asal muasal Repository Pattern, agar kita tahu kenapa ia diciptakan dan untuk menyelesaikan masalah seperti apa.

Martin Fowler pada https://martinfowler.com/eaaCatalog/repository.html

Eric Evans dalam bukunya Domain-Driven Design

Intinya, Repository Pattern adalah gimana kita membuat abstraksi (dengan suatu class) untuk mengenkapsulasi (menyembunyikan) query.
Ketika kita punya query yang kompleks, maka Repository Pattern ini dapat mengurangi kompleksitas dan memberikan reusability terhadap pengambilan data.
Sehingga bukan business logic yang kita taruh Repository, tapi hanya logic yang langsung berkaitan dengan interaksi database (query-query seperti select atau create)

Misal kita punya model:

Tweet.php

class Tweet extends Eloquent {
protected $fillable = [‘tweet’, ‘user_id’];
public function user()
{
return $this->belongsTo(‘App\User’, ‘user_id’);
}
}

Kemudian kita ingin mengambil latest tweet dari suatu user, sehingga kita akan buat query seperti ini:

UserController.php

class UserController extends Controller {
public function getTweets(Request $request) {
$user = User::find($request->user_id);
$latestTweets = Tweet::where('user_id', $user->id)->orderBy('created_at', 'desc');
}
}

Maka dengan Repository Pattern, kita akan merefactor controller diatas menjadi:

TweetRepository.php

class TweetRepository {
protected $model;
public function __construct(Tweet $tweet) {
$this->model = $tweet;
}
public function getLatestTweets($userId) {
return $this->model->where('user_id', $userId)->orderBy('created_at', 'desc');
}
}

UserController.php

class UserController extends Controller {
protected $tweetRepository;
public function __construct(TweetRepository $tweetRepository) {
$this->tweetRepository = $tweetRepository;
}
public function getTweets(Request $request) {
$user = User::find($request->user_id);
$latestTweets = $this->tweetRepository->getLatestTweets($user->id);
}
}

Dengan begitu, kita akan mendapatkan manfaat:

- Lebih readable, karena getLatestTweets() jelas lebih mudah dibaca daripada Tweet::where('user_id', $user->id)
- Reusable. Kita bisa menggunakan query untuk get latest tweets ditempat lain.
- Testable. Kita bisa melakukan unit test. Karena Repository gampang di mock daripada Eloquent.

Trus gimana kalau butuh logic sebelum interaksi ke database?

Menurutku, kita harus buat satu layer tambahan lagi yang berfungsi untuk meletakkan business logic dan mengelaborasi interaksi antar Repository.
Karena meletakkan business logic pada controller juga tidak tepat.

Kita sebut saja layer Service.

TweetService.php

class TweetService
{
protected $tweetRepository;
public function __construct(TweetRepository $tweetRepository)
{
$this->tweetRepository = $tweetRepository;
}
public function getTweets($userId)
{
return $this->tweetRepository->getLatestTweets($userId);
}
}

Jadi business logic nantinya akan diletakkan di layer service. Dan controller nanti akan manggil service ini, bukan langsung ke repository.
Dengan begitu, business logic pada service bisa kita unit test dan Repository yang dipanggil dalam service bisa di mock dengan mudah.

Contoh kasus lain penggunaan Repository Pattern

Pernah kejadian dalam suatu projek API-based, backend menggunakan Java dan frontend menggunakan PHP.
Agar frontend tidak perlu menunggu backend menyelesaikan API, maka frontend membuat Repository yang return data dummy sesuai dengan response API backend yang nantinya akan digunakan.

TweetRepository.php

class TweetRepository {
public function getLatestTweets($userId) {
return [[
'id' => 1,
'tweet' => 'sip',
'user_id' => 1,
'created_at' => null,
'updated_at' => null
]];
}
}

Contoh-contoh diatas memang terlihat sederhana dan kurang bermanfaat. Tapi di projek besar, kita akan bertemu dengan kompleks query atau beberapa cara berbeda untuk save data, dan manfaat Repository sebagai abstraksi database menjadi lebih terasa. Juga Repository memudahkan kita melakukan mocking interaksi database saat unit test. Yang penting you got the point, right?

Untuk contoh Unit Test pada service dan repository, bisa dilihat disini

--

--