Đăng ngày 21 tháng 10, 2025 • 9 phút đọc
Hầu hết các lập trình viên senior sẽ khuyên bạn nên bao bọc EF Core bên trong các giao diện repository của riêng bạn.
Nhưng bạn đã bao giờ tự hỏi: tại sao chúng ta cần một repository trên một repository?
Trong các dự án thực tế, lời khuyên này thường dẫn đến việc viết rất nhiều code boilerplate thừa thãi và tạo ra các giải pháp được thiết kế quá phức tạp.
Mỗi tính năng giờ đây cần nhiều thời gian hơn để triển khai và bảo trì so với mức cần thiết.
Có một cách tốt hơn.
Mục lục
Tại Sao Bạn Không Cần Repository Trong EF Core
Một trong những vấn đề phổ biến nhất là các Repository có xu hướng phát triển nhanh chóng khi yêu cầu kinh doanh thay đổi.
Khi ứng dụng của bạn còn nhỏ, sử dụng Pattern Repository có vẻ dễ dàng.
Điều bắt đầu như một thao tác CRUD đơn giản với 4 phương thức nhanh chóng trở thành một lớp lớn với các truy vấn đọc và ghi cơ sở dữ liệu cho mọi trường hợp có thể.
Khi domain của bạn phát triển, bạn phải đối mặt với quyết định quan trọng: bạn có nên tạo Repository cho mỗi entity không?
Mỗi yêu cầu kinh doanh mới có nghĩa là thêm một phương thức khác vào Repository. Theo thời gian, bạn kết thúc với các lớp đầy các phương thức tương tự.
Điều gì xảy ra khi bạn có nhiều thực thể?
Khi làm việc với nhiều thực thể khác nhau, việc tuân theo cách tiếp cận truyền thống dẫn đến việc tạo ra nhiều giao diện repository khác nhau.
Nhưng điều gì xảy ra khi bạn cần lấy các thực thể liên quan cùng nhau? Như:
- Lấy một shipment với tất cả các item của nó
- Lấy một order với các shipment liên quan
- Lấy dữ liệu lịch sử shipment tải nhiều thực thể
Các phương thức đa thực thể này thuộc về đâu?
Nhiều nhà phát triển cuối cùng có rất nhiều repository mà không làm được gì đủ. Và khi bạn triển khai một tính năng mới, bạn bắt đầu nghĩ về việc thêm một phương thức mới vào một trong N repository.
Các lý do phổ biến cho Repository:
1. “Chúng ta có thể chuyển cơ sở dữ liệu sau này”
Đây là lập luận phổ biến nhất.
Nhưng bạn thực sự chuyển từ cơ sở dữ liệu này sang cơ sở dữ liệu khác trong production bao nhiêu lần?
Trong 99% trường hợp, bạn sẽ không cần chuyển cơ sở dữ liệu. Tuy nhiên, ngay cả khi bạn chuyển từ một cơ sở dữ liệu SQL này sang một cơ sở dữ liệu SQL khác (ví dụ: Postgres → SQL Server), 95%+ code của bạn trong EF Core sẽ không thay đổi.
2. “Nó làm cho việc testing dễ dàng hơn”
Một số người cho rằng mocking một repository dễ hơn mocking một DbContext.
Nhưng điều này che giấu một vấn đề lớn hơn: bạn đang test một abstraction của một abstraction.
Mocking repository thường dẫn đến các test mỏng manh không phản ánh hành vi truy vấn thực. Thay vào đó, tốt hơn là sử dụng EF Core thực với cơ sở dữ liệu trong bộ nhớ hoặc thậm chí tốt hơn là viết integration tests.
3. “Nó thực thi sự tách biệt mối quan tâm”
Các Repository thường được sử dụng trong các kiến trúc N-Layered và Clean Architecture để giữ cho business layer tách biệt khỏi EF Core.
Nhưng trong thực tế, sự tách biệt này tạo ra nhiều nhầm lẫn hơn là rõ ràng.
Cách Sử Dụng EF Core Không Cần Repository
DbContext của EF Core đã triển khai các pattern Repository và Unit of Work, như được nêu trong summary code chính thức của DbContext.
Khi chúng ta tạo một repository trên EF Core, chúng ta tạo một abstraction trên một abstraction, dẫn đến các giải pháp được thiết kế quá phức tạp.
Mỗi DbSet<TEntity> trong DbContext của bạn đại diện cho một tập hợp các entity, giống như một repository điển hình.
Nó cho phép bạn:
- Truy vấn dữ liệu sử dụng LINQ
- Thêm, cập nhật và xóa entity
- Chiếu dữ liệu sang các loại khác
Inject DbContext Trong Các Service Hoặc Use Cases
Thay vì inject IShipmentRepository, IShipmentItemRepository và IOrderRepository, chỉ cần inject DbContext.
Sử Dụng EF Core Trực Tiếp Trong Ứng Dụng N-Layered
Kiến trúc N-Layered vẫn rất phổ biến trong các codebase.
Trong kiến trúc N-Layered, Application Layer của bạn nên chứa logic kinh doanh và các use cases. Nó nên phối hợp workflows, thực thi chính sách và gọi domain model hoặc infrastructure.
Sử Dụng EF Core Trực Tiếp Trong Clean Architecture Và Vertical Slice Architecture
Sử Dụng EF Core Trực Tiếp Trong Clean Architecture
Clean Architecture nhằm mục đích tách biệt các mối quan tâm của ứng dụng thành các lớp riêng biệt, thúc đẩy tính gắn kết cao và mức độ kết hợp thấp.
Nhưng khi thời gian trôi qua, Clean Architecture đã phát triển thành một cách tiếp cận thực tế hơn: nơi các nhà phát triển đồng ý rằng họ có thể sử dụng EF Core bên trong các use cases của Application.
Sử Dụng EF Core Trực Tiếp Trong Vertical Slice Architecture
Vertical Slice Architecture tập trung vào các tính năng, không phải các lớp.
Tôi tin rằng sự phát triển tự nhiên của Clean Architecture, với các thư mục tính năng của nó, đã dẫn đến sự biến đổi của nó thành Vertical Slice Architecture.
Sử Dụng Specification Pattern Với EF Core
Một trong những tùy chọn được đề cập ở trên để tránh trùng lặp mã khi sử dụng EF Core trực tiếp là sử dụng Specification Pattern.
Specification Pattern là một cách để mô tả dữ liệu bạn muốn từ cơ sở dữ liệu của mình bằng cách sử dụng các lớp nhỏ, có thể tái sử dụng được gọi là “specifications”.
Mỗi Specification đại diện cho một bộ lọc hoặc một quy tắc có thể được áp dụng cho một truy vấn. Điều này cho phép bạn xây dựng các truy vấn phức tạp bằng cách kết hợp các lớp đơn giản, dễ hiểu.
Specification Pattern mang lại các lợi ích sau:
- Tính tái sử dụng: Bạn có thể viết một specification một lần và sử dụng nó ở bất cứ đâu trong dự án của mình
- Sự kết hợp: Bạn có thể kết hợp hai hoặc nhiều specifications để tạo các truy vấn nâng cao hơn
- Tính testable: Specifications là các lớp trên EF Core (hoặc bất kỳ ORM nào khác), vì vậy bạn có thể cover chúng bằng unit test, hoặc thậm chí tốt hơn là integration test
- Sự tách biệt mối quan tâm: Logic truy vấn của bạn được tách biệt khỏi code truy cập dữ liệu
Testability Với EF Core
Một lý do phổ biến mà các nhà phát triển đưa ra để tạo repository là testability.
Lập luận là: “Nếu tôi bao bọc EF Core trong các repository, tôi có thể mock các repository trong unit test.”
Nhưng đây là thực tế:
- Mocking repository thường dẫn đến hành vi giả không khớp với EF Core
- Các test của bạn trở nên mỏng manh và ít giá trị hơn
- Bạn không thực sự test các truy vấn của mình, đây thường là phần quan trọng nhất
Thay vì mocking, bạn nên viết các test EF Core thực.
Tùy chọn 1: Sử Dụng EF Core InMemory Provider
EF Core có một nhà cung cấp cơ sở dữ liệu trong bộ nhớ, cho phép bạn chạy test mà không cần cơ sở dữ liệu vật lý.
Tùy chọn 2: Viết Integration Tests
Đây là tùy chọn yêu thích của tôi.
Viết các test giao tiếp với cơ sở dữ liệu thực là cách tốt nhất để đảm bảo ứng dụng của bạn hoạt động như mong đợi.
Tại sao điều này tốt hơn mocking repository:
- Các mock nói dối: chúng không sao chép bản dịch LINQ-to-SQL, eager loading hoặc hành vi tracking của EF Core
- Các test EF Core thực bắt các vấn đề thực: như các join không chính xác, các projection xấu hoặc các Include bị thiếu
- Các integration test cover nhiều hơn: đảm bảo không chỉ code của bạn hoạt động, mà còn đảm bảo DB schema của bạn là chính xác
Khi Bạn Vẫn Có Thể Cần Một Repository Tùy Chỉnh
Cho đến nay, chúng ta đã lập luận rằng hầu hết thời gian bạn không cần repository với EF Core. Nhưng giống như mọi quy tắc, có những ngoại lệ.
Đây là các trường hợp nơi một repository tùy chỉnh có thể có ý nghĩa:
- Các truy vấn rất phức tạp được sử dụng ở nhiều nơi
- Quy ước của nhóm hoặc ràng buộc dự án
- Các mối quan tâm hạ tầng cross-cutting
- Tích hợp bên ngoài
- Khi sử dụng Dapper
Tóm Tắt
Hãy tóm tắt các điểm rút ra chính:
- EF Core đã triển khai Repository và Unit of Work.
DbSet<TEntity>là repository của bạn.DbContext.SaveChangesAsync()là unit of work của bạn. - Các Repository thường thêm độ phức tạp không cần thiết. Chúng dẫn đến các repository béo, quá nhiều repository nhỏ hoặc các truy vấn trùng lặp trên các service.
- Sử dụng EF Core trực tiếp trong các service ứng dụng, handler hoặc vertical slice của bạn. Điều này giữ cho code của bạn đơn giản hơn, tập trung hơn và dễ bảo trì hơn.
- Sử dụng Specification Pattern để tái sử dụng truy vấn. Nó tránh trùng lặp và giữ các truy vấn phức tạp có thể kết hợp.
- Testing hoạt động tốt mà không cần repository. Sử dụng EF Core InMemory hoặc integration test – thay vì mocking repository.
- Các Repository vẫn có các trường hợp sử dụng đặc biệt. Đối với các truy vấn phức tạp được chia sẻ, các mối quan tâm cross-cutting hoặc truy cập dữ liệu đa nguồn, một repository có thể hữu ích.
Trong các ứng dụng .NET hiện đại, sử dụng EF Core trực tiếp trong lớp ứng dụng hoặc vertical slice của bạn thường là lựa chọn sạch nhất, đơn giản nhất và thực tế nhất.
Các Repository không chết – nhưng chúng không còn là mặc định. Chỉ sử dụng chúng khi chúng thực sự thêm giá trị.
Không có một cách đúng duy nhất để viết phần mềm; bạn cần chọn bất cứ thứ gì hoạt động tốt nhất trong mỗi dự án và trường hợp cụ thể.



