+3

Thiết kế tầng Repository và Service trong CakePHP 4 cho ứng dụng lớn

Giới thiệu

Hello anh em lâu rồi mới quay lại viết bài. Hôm nay mình chia sẻ về chủ đề tách các tầng repo và service trong Cakephp 4 . Nội dung chỉ là mình tìm hiểu và chia sẻ để học và lưu giữ lại . Nếu Có thắc mắc hay góp ý gì cho mình thì mọi người comment để cùng chia sẻ học hỏi và phát triển nhé .

1. Repository là gì ?

Trong lập trình phần mềm, Repository là một mẫu thiết kế (design pattern) được sử dụng để trừu tượng hóa và tách biệt phần lưu trữ dữ liệu (data storage) khỏi các đối tượng (objects) trong hệ thống. Repository pattern cung cấp một cách tiếp cận chung và trừu tượng cho việc truy cập dữ liệu, giúp dễ dàng thay đổi cách lưu trữ và truy xuất dữ liệu mà không cần thay đổi các đối tượng sử dụng dữ liệu đó.

Trong các ứng dụng sử dụng cơ sở dữ liệu (database), Repository pattern thường được sử dụng để truy xuất và lưu trữ dữ liệu, tương tác với các bảng trong cơ sở dữ liệu. Các phương thức của Repository thường bao gồm các thao tác cơ bản như tạo, đọc, sửa, xóa (CRUD - Create, Read, Update, Delete), và các phương thức truy vấn dữ liệu khác.

Repository pattern là một phần của SOLID design principles, giúp tăng tính mô đun hóa, giảm sự phụ thuộc giữa các thành phần trong hệ thống, và dễ dàng bảo trì và thay đổi phần lưu trữ dữ liệu trong tương lai.

2. Service là gì ?

Trong lập trình, Service là một thành phần của kiến trúc phần mềm (architecture) dùng để xử lý các tác vụ nghiệp vụ của ứng dụng. Nói cách khác, Service là một lớp (class) có chức năng xử lý một số tác vụ nhất định liên quan đến nghiệp vụ của ứng dụng. Service có thể gọi các đối tượng, phương thức từ các lớp khác như Repository, Model,... và trả về kết quả cho các lớp Controller, View hoặc các Service khác.

Các tác vụ nghiệp vụ có thể bao gồm xử lý dữ liệu, kiểm tra và xác thực dữ liệu, gửi email, tạo báo cáo, truy vấn cơ sở dữ liệu, tính toán, tích hợp các dịch vụ khác,... Service giúp tách biệt các tác vụ nghiệp vụ khỏi các lớp khác trong kiến trúc MVC, đơn giản hóa quá trình phát triển và bảo trì ứng dụng.

Thôi lươn khươn tí rồi giờ mình sẽ đi vào chi tiết luôn nhé .

Nội dung Chính

Trong Cakephp 4 thì có hỗ trợ tách cái phần Repository và Service . Bạn có thể sử dụng tầng repository để thực hiện các truy vấn đến cơ sở dữ liệu và sử dụng tầng service để viết logic xử lý dữ liệu. Dưới đây là một ví dụ về cách sử dụng abstract class và interface để tạo base repository và service trong CakePHP 4:

1. Tạo Base cho Repository


namespace App\Repositories\Interfaces;

use Cake\ORM\Query;

interface IBaseRepository
{
    public function find(): Query;

    public function save($entity): bool;
    
    // ....
}

namespace App\Repositories\Eloquents;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

use Repositories\Interfaces\IBaseRepository;

abstract class BaseRepository implements IBaseRepository
{
    protected $table;

    public function __construct(Table $table)
    {
        $this->table = $table;
    }

    public function find(): Query
    {
        return $this->table->find();
    }

    public function save($entity): bool
    {
        return (bool) $this->table->save($entity);
    }
}

2. Tạo Repo Ví dụ

Ở dứoi đây khi sử dụng service bạn hãy tạo mới 1 Repo nhé , Mình sẽ tạo 1 ArticleRepository ví dụ :

namespace App\Repositories\Interfaces;

use Cake\ORM\Query;

interface IArticleRepository extends IBaseRepository
{
    public function find(): Query;

    public function save($entity): bool;
    
    // ....
}
namespace App\Repositories\Eloquents;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

use App\Repositories\Interfaces\IArticleRepository;

class ArticleRepository extends BaseRepository implements IArticleRepository
{
    protected $table;

    public function __construct(Table $table)
    {
        $this->table = $table;
    }

    public function find(): Query
    {
        return $this->table->find();
    }

    public function save($entity): bool
    {
        return (bool) $this->table->save($entity);
    }
}

Lưu ý rằng bạn cần đăng ký repository và service với DI container trước khi sử dụng chúng. Bạn có thể đăng ký bằng cách sử dụng phương thức set() của DI container. Ví dụ, nếu bạn muốn đăng ký một instance của ArticleRepository .

Trong file src/Application.php, bạn có thể tạo ra các thuộc tính để lưu trữ các cấu hình của các repository. Ví dụ:


protected $repositories = [
        'ArticleRepository',
        'UserRepository',
        'CommentRepository',
    ];
    
public function bootstrap(): void
{
    // Register repositories
    $this->registerRepositories();
}

private function registerRepositories(): void
{
   foreach ($this->repositories as $repository) {
        $interface = 'App\Repositories\Interfaces\I' . $repository;
        $class = 'App\Repositories\Eloquents\\' . $repository;

        // Register repository implementation with DI container
        $this->getContainer()->set($class, function ($container) use ($class) {
            return new $class(/* pass in any necessary dependencies */);
        });

        // Register interface with DI container
        $this->getContainer()->set($interface, $class);
    }

}

3. Tạo Service


namespace App\Services;

use App\Repositories\Interfaces\IArticleRepository as ArticleRepository;

class ArticleService
{
    private $articleRepository;

    public function __construct(ArticleRepository $articleRepository)
    {
        $this->articleRepository = $articleRepository;
    }

    public function getAll()
    {
        return $this->articleRepository->find()->all();
    }

    public function save($data)
    {
        // Validate data here

        $article = $this->articleRepository->newEntity($data);

        $this->articleRepository->save($article);

        return $article;
    }

    //... 
}

Chúng ta cũng sẽ đang kí service trong src/Application.php


protected $services = [
    'ArticleService',
    'UserService',
    'CommentService',
];

public function bootstrap(): void
{

    // Register repositories - này là ở step trên khi đang kí repo
    $this->registerRepositories();

    // Register services
    $this->registerServices();
}

protected function registerServices(): void
{
    foreach ($this->services as $service) {
       //$interface = 'App\\Service\\' . $service . 'Interface'; // theo chuẩn nguyên lý SOLID thì service cũng sẽ phải tạo ra Interface , 
        $class = 'App\\Service\\' . $service;

        // Register service implementation with DI container
        $this->getContainer()->set($class, function ($container) use ($class) {
            return new $class(/* pass in any necessary dependencies */);
        });

        // Register interface with DI container
        $this->getContainer()->set($interface, $class);
    }
}

4. DI Service vào Controller

  1. Khai báo biến $serviceName trong phương thức __construct của controller, ví dụ:

namespace App\Controller;

use App\Service\ArticleService;
use Cake\Http\Controller;

class ArticlesController extends Controller
{
    private $articleService;

    public function __construct(ArticleService $articleService)
    {
        $this->articleService = $articleService;
    }

    // ...
}

  1. Sử dụng biến $articleService trong các phương thức của controller, ví dụ:

public function index()
{
    $articles = $this->articleService->getAllArticles();

    $this->set(compact('articles'));
}

Trong phương thức index, chúng ta sử dụng biến $articleService để gọi phương thức getAllArticles() của service.Chú ý rằng, khi sử dụng Dependency Injection trong CakePHP 4, bạn không cần phải tạo ra instance của Service bằng cách sử dụng new mà thay vào đó, CakePHP sẽ tự động tạo ra instance và inject vào Controller khi cần thiết.Trong ví dụ trên, khi tạo instance của ArticlesController, CakePHP sẽ tự động tạo ra instance của ArticleService và truyền vào phương thức __construct.

Kết thúc

hi vọng vào chia sẻ trên sẽ giúp ích cho các bạn . Thanks for watching.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí