Repository Pattern

Repository (kho lưu trữ) là mẫu thiết kế tạo một tầng trung gian giữa domain và data source, cung cấp các phương thức kiểu “một bộ sưu tập đối tượng” để quản lý entity. Nó cho phép Domain Model - Mô hình miền nghiệp vụ “làm việc” với hạ tầng dữ liệu mà không cần biết dữ liệu được lưu ở đâu, như thế nào.

Martin Fowler: Repository đóng vai trò như một tập hợp hướng đối tượng, cho phép tách một chiều phụ thuộc giữa tầng domain và tầng mapping dữ liệu.

🧩 Ý tưởng cốt lõi: interface ở domain, implementation ở hạ tầng

  • Với domain code: repository là một interface (cổng) để truy xuất đối tượng.
  • Với infrastructure code: repository là chỗ cài đặt chi tiết (dùng ORM, SQL, file…).
// Ở Domain: chỉ là hợp đồng (contract)
interface OrderRepository
{
    public function save(Order $order): int;      // lưu, trả về ID
    public function getById(int $orderId): Order; // tìm theo ID, ném exception nếu không thấy
}
// Ở Infrastructure: cài đặt cụ thể
final class SqlOrderRepository implements OrderRepository
{
    public function save(Order $order): int
    {
        $data = [
            'ebook_id' => $order->getEbookId(),
            'email'    => $order->getEmailAddress(),
            'quantity' => $order->getQuantity(),
            'amount'   => $order->getTotalAmount(),
        ];
        return DB::table('orders')->insertGetId($data);
    }
    // getById(): query DB rồi tái tạo Order entity
}

💪 Sức mạnh: decoupling & thay thế được

Domain chỉ biết gọi orderRepository->save($order), còn $order đi đâu, lưu ra sao tùy phiên bản cụ thể. Nhờ đó ta có thể có nhiều implementation cùng thỏa interface:

  • SqlOrderRepository (MySQL), MongoOrderRepository (MongoDB)
  • InMemoryOrderRepository (lưu vào mảng, dùng khi test)

Khi chạy thật, ta bind interface với implementation thật; khi test, bind với bản in-memory. Đổi cách lưu (vd sang gọi API) → chỉ viết RemoteOrderRepository mới và bind thay, domain code không đổi. Đây chính là ứng dụng của Dependency Inversion Principle.

💡 Mẹo thiết kế

Cứ giả định đã có sẵn thành phần mình cần (orderRepository->save($order) cho tiện), rồi mới định nghĩa interface đúng như mong muốn, sau đó lo cài đặt.

Liên quan