+8

Kiến trúc hệ thống trên Laravel – phần 6

Các bài viết trong series

Kiến trúc hệ thống trên Laravel – phần 1 : Tại sao phải áp dụng architect vào trong Laravel Kiến trúc hệ thống trên Laravel – phần 2 : OOP, Interface, Dependency Injection, IoC Kiến trúc hệ thống trên Laravel – phần 3 : Phân tích sâu vào việc sử dụng interface Kiến trúc hệ thống trên Laravel – phần 4 : Design Pattern – Decorator Kiến trúc hệ thống trên Laravel – phần 5 : Design Pattern – Adapter Kiến trúc hệ thống trên Laravel – phần 6 : Design Pattern – Repository Kiến trúc hệ thống trên Laravel – phần 7 : Design Pattern – Factory Kiến trúc hệ thống trên Laravel – phần 8 : Advance component trong Laravel Kiến trúc hệ thống trên Laravel – phần 9 : Mô hình kiến trúc cụ thể Part 1 Kiến trúc hệ thống trên Laravel – phần 10 : Mô hình kiến trúc cụ thể Part 2

Xin chào các bạn. Mình vào nghề lập trình cũng đã lâu, cũng có 1 số hiểu biết coi như là nâng cao về framework Laravel. Nên hôm nay mình xin chia sẻ 1 chút về kiến trúc hệ thống của mình được xây dựng trên Laravel như thế nào. Mong rằng có thể giúp ích cho các bạn 😃.


Repository thì mình đoán là khá quen thuộc với các bạn rồi. Nguyên lý thì khá đơn giản, cũng giống interface thôi, cần phải chia nhỏ component và làm chúng loosely couple với nhau.

Bạn hãy tưởng tượng 1 request lên server thường sẽ qua những công đoạn xử lý nào?

Request -> check authen -> check author -> validation -> process raw request data -> update database -> render view -> Response

Lí tưởng nhất là bạn coi từng phần là component riêng và sử dụng interface vào để linh hoạt hóa các component đó.

Với phần update database, chúng ta sẽ sử dụng repository để làm.

Ok, giờ chúng ta đi vào cụ thể nhé

Code không repository (giả sử chúng ta có Eloquent Model Book)

class BookController extends BaseController
{
    public function index()
    {
        $books = Book::paginate(10);
        return view('books.inde');
    }

    public function store(Request $request)
    {
        Book::insert($request->all());
        return redirect()->route('books.index');
    }
}

Cách code này có vấn đề gì không? Xin thưa với bạn rằng nó chẳng có vấn đề gì cả 😄, chỉ là nó không linh hoạt mà thôi -> bạn đã gắn chặt controller với eloquent model mà thôi -> nếu bạn muốn sử dụng ORM nào khác thì bạn phải thay đổi lại tất cả những chỗ sử dụng Eloquent mà thôi 😄. Cost của việc này thì mình đã phân tích ở những bài trước khá nhiều rồi ^^.

Vậy sử dụng repository thì làm thế nào ^^.

Cũng như mọi khi thôi: tạo interface, tạo class implement interface đó, inject inteface vào client (ở ví dụ này client là controller) và bindding trong service provider

Tạo inteface

interface BookRepositoryInterface
{
    public function paginate($quantity);
    public function persist($data);
}

Tạo class thực thi interface

class BookRepository implements BookRepositoryInterface
{
    protected $model;

    public function __construct(Book $model)
    {
        $this->model = $model;
    }
    public function paginate($quantity)
    {
        return $this->model->paginate($quantity);
    }

    public function persist($data)
    {
        return $this->model->insert($data);
    }
}

(Bạn thấy đấy, mình inject eloquent model Book vào trong construct và sử dụng nó ở trong class chứ không sử dụng static function như ví dụ trên -> nếu bạn sử dụng ORM nào khác – không phải eloquent – không có các method paginate() chẳng hạn, bạn có thể sử dụng Adapter để kết nối trong trường hợp này -> xin hãy xem lại bài trước để nhớ về adapter nhá)

Inject interface vào client (controller)

class BookController extends BaseController
{
    protected $repository;
    public function __construct(BookRepositoryInterface $repository)
    {
        $this->repository = $repository;
    }
    public function index()
    {
        $books = $this->repository->paginate(10);
        return view('books.index');
    }

    public function store(Request $request)
    {
        $this->repository->persist($request->all());
        return redirect()->route('books.index');
    }
}

Chú ý: luôn luôn inject interface vào trong client, khi đó bạn mới linh hoạt bằng cách bindding trong service provider được nhé

Cuối cùng là bindding

$this->app->bind('BookRepositoryInterface', 'BookRepository');

Nếu bạn follow series này từ đầu đến bài này thì tất cả những thao tác kể trên bạn đã rất là quen thuộc rồi nhỉ ^^.

Tác dụng lớn nhất của repository là bạn không phụ thuộc vào 1 biến thể tương tác với database này cả -> ví dụ như bạn hoàn toàn không phụ thuộc vào Eloquent mà có thể sử dụng ORM khác (mình recommend các bạn nghiên cứu Doctrine ORM)

Ngoài ra tùy sự sáng tạo của bạn mà bạn có thể làm được nhiều việc khác với repository, ví dụ như viết 1 lớp cache layer cho repository với decorator ^^ (cái này mình đã có riêng 1 bài hướng dẫn về decorator rồi nhá 😄). Mình ví dụ luôn nhé

Viết 1 class mới sử dụng cache để decorate lên repository

use Illuminate\Cache\Repository as Cache;

class BookCacheRepository implements BookRepositoryInterface
{
    protected $repository;
    protected $cache;

    public function __construct(BookRepositoryInterface $repository, Cache $cache)
    {
        $this->repository = $repository;
        $this->cache = $cache;
    }
    public function paginate($quantity)
    {
        return $this->cache->tags('books')->rememberForever('books.paginate', function() {
            return $this->repository->paginate($quantity);
        });
    }

    public function persist($data)
    {
        return $this->repository->persist($data);
    }
}

Các bạn thấy đấy, mình đã wrap cache của Laravel ở các method truy vấn dữ liệu (nôm na là thực hiện select query), như trường hợp trên là mình sử dụng trong method paginate()

Giờ thay đổi bindding nữa là xong

$this->app->bind(BookRepositoryInterface::class, function($app){
    $bookRepo = new BookRepository(new Book());
    return new BookCacheRepository($bookRepo, $this->app['cache.store']);
});

Cũng như mọi khi, chúng ta thấy chúng ta có thể bổ sung tính năng mà hoàn toàn không phải động vào code cũ


Bài viết đến đây là kết thúc, hi vọng cung cấp được kiến thức bổ ích cho bạn. Xin cảm ơn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.