2009-09-11 39 views
35

Tôi đã khám phá BDD/DDD và kết quả là cố gắng đưa ra cách triển khai đúng mô hình Repository. Cho đến nay, thật khó để tìm thấy một sự đồng thuận về cách tốt nhất để thực hiện điều này. Tôi đã cố gắng để đun sôi nó xuống các biến thể sau đây, nhưng tôi không chắc chắn đó là cách tiếp cận tốt nhất.Cách tốt nhất để triển khai Mẫu Kho lưu trữ là gì?

Để tham khảo, tôi đang xây dựng một ứng dụng ASP.MVC với NHibernate làm giao diện người dùng.

public interface IRepository<T> { 
     // 1) Thin facade over LINQ 
     T GetById(int id); 
     void Add(T entity); 
     void Update(T entity); 
     void Remove(T entity); 
     IQueryable<T> Find(); 
     // or possibly even 
     T Get(Expression<Func<T, bool>> query); 
     List<T> Find(Expression<Func<T, bool>> query); 
} 

public interface IRepository<T> { 
     // 2) Custom methods for each query 
     T GetById(int id); 
     void Add(T entity); 
     void Update(T entity); 
     void Remove(T entity); 
     IList<T> FindAll(); 
     IList<T> FindBySku(string sku); 
     IList<T> FindByName(string name); 
     IList<T> FindByPrice(decimal price); 
     // ... and so on 
} 

public interface IRepository<T> { 
     // 3) Wrap NHibernate Criteria in Spec pattern 
     void Add(T entity); 
     void Update(T entity); 
     void Remove(T entity); 
     IList<T> FindAll(); 
     IList<T> FindBySpec(ISpecification<T> specification); 
     T GetById(int id); 
} 


public interface IRepository<T> { 
     // 4) Expose NHibernate Criteria directly 
     T GetById(int id); 
     void Add(T entity); 
     void Update(T entity); 
     void Remove(T entity); 
     IList<T> FindAll(); 
     IList<T> Find(ICriteria criteria); 
     // .. or possibly 
     IList<T> Find(HQL stuff); 
} 

những suy nghĩ ban đầu của tôi là rằng

1) là rất lớn từ một điểm hiệu quả của xem, nhưng tôi có thể nhận được vào rắc rối khi mọi thứ trở nên phức tạp hơn.

2) có vẻ rất tẻ nhạt và có thể kết thúc với một lớp học rất đông đúc, nhưng nói cách khác cung cấp mức độ tách biệt cao giữa logic miền và lớp dữ liệu mà tôi thích.

3) có vẻ khó khăn ở phía trước và nhiều công việc hơn để viết truy vấn, nhưng giới hạn ô nhiễm chéo chỉ với lớp Thông số.

4) Yêu cầu ít nhất của tôi, nhưng có thể trực tiếp nhất và có thể là hiệu quả nhất cho các truy vấn phức tạp, mặc dù nó đặt rất nhiều trách nhiệm về mã gọi.

+0

+1 cho câu hỏi kịp thời. Tôi cũng đã vật lộn với mẫu Repository. – TrueWill

+3

Có lẽ không phải những gì bạn đang tìm kiếm, nhưng tôi thấy việc triển khai mẫu Spec này thực sự tuyệt vời: http://mathgeekcoder.blogspot.com/2008/07/advanced-domain-model-queries-using.html. Chủ yếu là vì nó cho phép bạn sử dụng Linq2Sql/Linq2NHibernate/etc. –

+0

Cảm ơn TrueWill và cảm ơn Martinho, điều đó thực sự liên quan rất tốt đến một chủ đề khác mà tôi đã đăng gần đây http: // stackoverflow.com/questions/1408553/đặc điểm kỹ thuật-pattern-vs-spec-in-bdd –

Trả lời

10

Tôi nghĩ rằng tất cả đều là lựa chọn tốt (ngoại trừ có thể 4 nếu bạn không muốn buộc mình vào nhibernate), và bạn dường như có ưu và nhược điểm cũng được phân tích để đưa ra quyết định dựa trên nỗ lực hiện tại của bạn . Đừng đánh bại chính mình quá về vấn đề này.

Tôi hiện đang làm việc trên một hỗn hợp giữa 2 và 3 tôi đoán:

public interface IRepository<T> 
{ 
     ... 
     IList<T> FindAll(); 
     IList<T> FindBySpec(ISpecification<T> specification); 
     T GetById(int id); 
} 

public interface ISpecificRepository : IRepository<Specific> 
{ 
     ... 
     IList<Specific> FindBySku(string sku); 
     IList<Specific> FindByName(string name); 
     IList<Specific> FindByPrice(decimal price); 
} 

Và đó cũng là một Repository (T) lớp cơ sở.

+1

Tôi nghi ngờ một chút # 2 sẽ leo vào mọi giải pháp. –

1

Tôi là fan bif của 1 vì tôi có thể tạo bộ lọc và phương pháp mở rộng phân trang hơn tôi có thể áp dụng cho các giá trị trả về cho phương thức Tìm kiếm IQueryable <>. Tôi giữ các phương thức mở rộng trong lớp dữ liệu và sau đó xây dựng trên bay trong lớp kinh doanh. Tất nhiên, khi hệ thống ổn định, tôi có tùy chọn để thực hiện các phương pháp Tìm cụ thể bằng cách sử dụng cùng một phương pháp mở rộng và tối ưu hóa bằng cách sử dụng Func <>.

5

Một trong những điều chúng tôi đang làm là tất cả các kho của chúng tôi có nhu cầu khác nhau vì vậy chúng tôi đang tạo ra một tập hợp các giao diện:

public interface IReadOnlyRepository<T,V> 
{ 
    V Find(T); 
} 

Trong ví dụ này chỉ đọc kho chỉ làm được từ cơ sở dữ liệu. Lý do cho T, V là V đại diện cho những gì được trả về bởi các kho lưu trữ và T đại diện cho những gì được thông qua tại, vì vậy bạn có thể làm một cái gì đó như thế này:

public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer> 
{ 
    public Customer Find(int customerId) 
    { 
    } 

    public Customer Find(string customerName) 
    { 
    } 
} 

tôi cũng có thể tạo giao diện riêng biệt cho Add , Cập nhật và Xóa. Bằng cách này, nếu kho lưu trữ của tôi không cần hành vi thì nó không thực hiện giao diện.

+0

Điều này thật thú vị. Tôi cần phải suy nghĩ về những lợi ích tiềm năng của việc sử dụng IReadonlyRepository , nhưng ngay từ cái nhìn đầu tiên, tôi rất thích nó. Cám ơn vì đã chia sẻ. –

+0

Nếu bạn thấy điều này hữu ích, bạn có thể bỏ phiếu cho câu trả lời của tôi không? –

1

Khi sử dụng NH với LINQ kho của bạn có thể là:

session.Linq<Entity>() 

Các thông số kỹ thuật là những thứ mà đối phó với:

IQueryable<Entity> 

Bạn có thể mặt tiền đi tất cả nếu bạn muốn, nhưng đó là một rất nhiều công việc nhàm chán để trừu tượng hóa một trừu tượng.

Đơn giản là tốt. Yah, NH thực hiện cơ sở dữ liệu, nhưng nó cung cấp nhiều mẫu hơn. Có khác hơn so với DAL phụ thuộc vào NH là xa một tội lỗi.

+0

Nhưng sử dụng phiên trực tiếp trong logic, trong khi đơn giản, sẽ vi phạm Inversion of Control và làm cho nó thực sự khó khăn để kiểm tra, nó sẽ không? –

+0

Phụ thuộc vào cách bạn cấu trúc mã của mình. Nếu bạn sử dụng mẫu Đơn vị công việc, thì ISession là UoW của bạn. Bạn có thể đặt một mặt tiền mỏng trên ISession nếu điều đó làm cho mọi thứ tốt hơn (tôi không). Vì vậy, bạn sau đó bạn còn lại với kho. Tôi xem kho như là rất cơ bản - có được đối tượng, lưu đối tượng, phơi bày các đối tượng như một bộ sưu tập (ISession làm tất cả điều này). Các truy vấn/thông số có thể được xử lý bởi IQueryable. Nếu bạn có logic miền kết thúc tùy thuộc vào ISession nó sẽ thay vào đó đã kết thúc tùy thuộc vào một Repository. Cả hai đều là những tình huống xấu như nhau. – anonymous

+0

Vì vậy, ISession có thể kết thúc là một chút của một loại 'làm mọi thứ' của giao diện. Đó là lý do tại sao đặt nó phía sau một giao diện khác có thể rõ ràng hơn một chút về cách nó đang được sử dụng (IUnitOfWork, IRepository , v.v ...). Tôi nghĩ rằng một phần là sở thích cá nhân, và tôi sẽ không dành nhiều thời gian cho kỹ thuật trừu tượng hoàn hảo. – anonymous

22

Ngoài ra còn có một lý lẽ tốt cho phương pháp "không có ở trên".

Sự cố với kho lưu trữ chung là bạn đang giả định rằng tất cả các đối tượng trong hệ thống của bạn sẽ hỗ trợ tất cả bốn hoạt động CRUD: Tạo, Đọc, Cập nhật, Xóa. Nhưng trong các hệ thống phức tạp, bạn có thể sẽ có các đối tượng chỉ hỗ trợ một vài thao tác. Ví dụ: bạn có thể có các đối tượng chỉ đọc hoặc các đối tượng được tạo nhưng không bao giờ được cập nhật.

Bạn có thể ngắt giao diện IRepository thành các giao diện nhỏ, cho Đọc, Xóa, v.v. nhưng điều đó khá nhanh chóng.

Gregory Young tạo lập luận tốt (từ góc nhìn phân lớp DDD/phần mềm) rằng mỗi kho lưu trữ chỉ hỗ trợ các hoạt động cụ thể cho đối tượng miền hoặc tổng hợp mà bạn đang làm việc. Đây là bài viết của anh ấy trên generic repositories.

Và để có chế độ xem thay thế, hãy xem Ayende blog post này.

+1

Tôi đồng ý. Tôi chỉ cần thêm rằng không có nghĩa là bạn không thể có một thực hiện chung, bạn chỉ cần không có một giao diện chung chung. Bạn có thể có một Kho lưu trữ trừu tượng (và không có giao diện IRepository ) với tất cả các phương thức được bảo vệ. Mỗi kho lưu trữ cụ thể sau đó có thể mở rộng nó và làm cho các phương thức mong muốn công khai để thực hiện giao diện ISpecificRepository. – svallory

+0

Thật vậy! Theo kinh nghiệm của tôi, cách tốt nhất để triển khai mẫu Repository là * không * thực hiện nó. Thay vào đó, hãy sử dụng một API ORM tốt (như JPA trong Java) và một lớp "ApplicationDatabase" đơn lẻ để làm cho nó dễ sử dụng hơn. Đó là sự lựa chọn của tôi cho một ứng dụng Java EE, anyway; Tôi thấy nó giữ mã ứng dụng đơn giản hơn, ngắn hơn và gắn kết hơn là có một loạt các lớp "EntityAbcRepository" bổ sung. –

1

Tôi tin rằng điều đó tùy thuộc vào nhu cầu của bạn. Điều quan trọng là phải suy nghĩ về kho lưu trữ kết hợp với các mẫu thiết kế khác mà bạn cân nhắc sử dụng. Cuối cùng, nó rất phụ thuộc vào những gì bạn mong đợi từ kho lưu trữ (những lý do chính để sử dụng nó) là gì.

Bạn có cần tạo lớp nghiêm ngặt (ví dụ bạn cần thay NHibernate bằng Entity Framework trong tương lai) không? Bạn có muốn viết kiểm tra đặc biệt cho các phương pháp kho lưu trữ?

Không có cách nào tốt nhất để tạo kho lưu trữ. Chỉ có một vài cách và nó chắc chắn là tùy thuộc vào bạn, điều gì là thiết thực nhất cho nhu cầu của bạn.

Các vấn đề liên quan