Thế tóm lại Repository Pattern để làm gì
Trong các cuộc phỏng vấn mình tham gia, hầu hết mọi người đều sử dụng Repository trong dự án, và mục đích sử dụng thường là "nhằm linh hoạt thay đổi database và tái sử dụng code". Câu hỏi đặt ra là:
Có bao nhiêu dự án bạn từng làm thực sự thay đổi database hoàn toàn thông qua Repository ?
Câu trả lời: "Rất hiếm khi". Việc thay đổi database bên dưới (từ Mysql sang MongoDB chẳng hạn) đòi hỏi chi phí chuyển đổi to đùng hơn là chỉ thay đổi cách implement interface. Thông thường các db sẽ bổ trợ nhau, chứ hiếm khi thay đổi hoàn toàn.Nếu để tái sử dụng code, thì câu hỏi đặt ra là:
Có nhất thiết phải tạo một lớp trừu tượng (interface) rồi implement lại như Repository ?. Rõ ràng có thể sử dụng Service Layer để tái sử dụng các phương thức đó (Và tất nhiên rồi, chúng ta sử dụng Eloquent ở đây phải không ?)
Ví dụ nhé: hàm findByABC()
=> bên trong là Model::query()->where('abc', 'something')->get()
hoàn toàn có thể được triển khai ở lớp Service layer và vẫn không thay đổi gì (Nếu mình không gọi nó là Repository nữa thì cũng ko ai nhận ra cả). Rồi, thế rốt cục là Repository nên được hiểu như thế nào??? Nói như ông thì chả nhẽ bỏ luôn Repository à ? Ae bình tĩnh đọc tiếp nhé.
"Repository Pattern" được định nghĩa trong Patterns of Enterprise Application Architecture (của Martin Fowler, các bạn nhớ phải kiếm cuốn này đọc nha) rằng nó:
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects
Oke, hiểu như thế này: Repository nên là một lớp trung gian giữa business logic và lớp hạ tầng (triển khai Database hoặc giao tiếp với external service => Eloquent là một ví dụ tiêu biểu cho triển khai giao tiếp DB lớp hạ tầng. Và đương nhiên bạn có thay thế Eloquent bằng khẩu vị của bạn (Doctrine chẳng hạn))
Hãy lấy một ví dụ: Bạn đang làm một dự án có đối tượng sản phẩm (Product). Tại layer nghiệp vụ (Domain) của bạn, bạn định nghĩa một đối tượng là Product. Hãy nhớ đoạn này, và bạn cũng đừng nhầm lẫn, đối tượng Product này khác hoàn toàn với Model Product của Laravel (cái đại diện để tương tác với database)
Tôi sẽ triển khai một interface ProductRepository như thế này:
interface ProductRepository {
public function get($id): Product;
public function save(Product $product): void;
public function delete(Product $product): void;
}
Interface này tôi sẽ đặt nó ở layer nghiệp vụ (nếu các bạn đã quen với các bài talk của mình, thì mình hay gọi nó là domain). Lớp nghiệp vụ hoàn toàn giao tiếp nội bộ bên trong nó, và nó "mù" hoàn toàn về việc triển khai (implement) lại interface này. (tức là nó méo cần biết các ông dev làm gì ở dưới cả, chuẩn phong cách BA nghiệp vụ chưa).
Đó là lý do vì sao trong meetup Hà Nội, chúng ta nhắc đến việc "Không sử dụng Eloquent". Clear hơn một chút, tôi "không" dùng Eloquent trong lớp nghiệp vụ, mà đẩy nó xuống lớp hạ tầng (Infrastructure) mà thôi. Điều này đem lại lợi ích quan trọng cho việc:
- Nghiệp vụ được rõ ràng, tách bạch khỏi technical design. Đối tượng Product của bạn kia có thể được đại diện bởi 3-4 bảng ở dưới database (và có thể bị thay đổi thường xuyên). Bóc tách thành 2 phần riêng biệt giúp bạn linh hoạt hơn nhiều trong thiết kế khi nghiệp vụ phức tạp.
- Linh hoạt trong triển khai: Yes, bạn không nghe nhầm đâu, Repository ngoài đại diện tương tác database, còn có thể được implement để gọi API (chuyện bình thường, như call ElasticSearch chẳng hạn). Việc bạn nâng cấp hạ tầng cũng không hề ảnh hưởng tới nghiệp vụ đã triển khai.
Tóm tắt:
Repository không phải ORM, đừng dùng nó như ORM (Vì như thế dùng thẳng ORM còn hơn). Repository tách biệt hoàn toàn khỏi Technical design, vì nó sẽ là cầu nối giữa nghiệp vụ và triển khai hệ thống.
Nhưng dự án của bạn khá đơn giản thì sao ?. Theo mình, nếu dự án nhỏ, thì không nên sử dụng Repository. Hãy tận dụng những tính năng mạnh mẽ của Laravel Model như Scope, Custom Query Builder, Macro... Nó cực kỳ linh hoạt và bạn sẽ không phải máy móc tạo interface mà không hiểu rốt cục nó để làm gì cả ?
PS: Mình đang muốn làm một số course free về những chủ đề này của Laravel (sẽ sâu hơn, có code demo), ae có hứng thú không ? Nếu có xin ae 1 tym ủng hộ nhé, cám ơn mọi người.