Sự khác biệt giữa lớp lưu trữ và lớp dịch vụ?


188

Trong các mẫu thiết kế OOP, sự khác biệt giữa Mẫu lưu trữ và Lớp dịch vụ là gì?

Tôi đang làm việc trên một ứng dụng ASP.NET MVC 3 và đang cố gắng tìm hiểu các mẫu thiết kế này, nhưng bộ não của tôi vẫn chưa nhận được ...

Câu trả lời:


327

Lớp lưu trữ cung cấp cho bạn mức độ trừu tượng bổ sung đối với truy cập dữ liệu. Thay vì viết

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

để có được một mục từ cơ sở dữ liệu, bạn sử dụng giao diện kho lưu trữ

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

và gọi Get(id). Lớp lưu trữ trưng bày các hoạt động CRUD cơ bản .

Lớp dịch vụ trưng ra logic nghiệp vụ, sử dụng kho lưu trữ. Dịch vụ ví dụ có thể trông như:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

Mặc dù List()phương thức kho lưu trữ trả về tất cả người dùng, nhưng ListUsers()IUserService chỉ có thể trả về những người dùng, người dùng có quyền truy cập.

Trong ASP.NET MVC + EF + SQL SERVER, tôi có luồng giao tiếp này:

Lượt xem <- Bộ điều khiển -> Lớp dịch vụ -> Lớp kho lưu trữ -> EF -> Máy chủ SQL

Lớp dịch vụ -> Lớp lưu trữ -> EF Phần này hoạt động trên các mô hình.

Lượt xem <- Bộ điều khiển -> Lớp dịch vụ Phần này hoạt động trên các mô hình xem.

BIÊN TẬP:

Ví dụ về luồng cho / Đơn hàng / ByClient / 5 (chúng tôi muốn xem thứ tự cho khách hàng cụ thể):

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

Đây là giao diện cho dịch vụ đặt hàng:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

Giao diện này trả về mô hình xem:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

Đây là giao diện thực hiện. Nó sử dụng các lớp mô hình và kho lưu trữ để tạo mô hình khung nhìn:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}

2
@Sam Striano: Như bạn có thể thấy ở trên, Kho lưu trữ của tôi trả về IQueryable. Điều này cho phép thêm bổ sung khi có điều kiện và thực thi hoãn lại trong lớp dịch vụ, không muộn hơn. Vâng, tôi sử dụng một hội đồng, nhưng tất cả các lớp này được đặt trong các không gian tên khác nhau. Không có lý do để tạo ra nhiều hội đồng trong các dự án nhỏ. Không gian tên và tách thư mục hoạt động tốt đẹp.
LukLed

81
Tại sao trở lại mô hình xem trong dịch vụ? không phải là dịch vụ giả sử nếu bạn có nhiều khách hàng (di động / web)? Nếu đó là trường hợp thì viewmodel có thể khác với các nền tảng khác nhau
Ryan

12
Đồng ý với @Ryan, lớp dịch vụ sẽ trả về đối tượng thực thể hoặc bộ sưu tập các đối tượng thực thể (không phải IQueryable). Sau đó, trên ui thực thể ánh xạ tới someViewModel của Automapper chẳng hạn.
Eldar

5
@Duffp: Bạn không phải tạo kho lưu trữ cho mọi thực thể. Bạn có thể sử dụng việc thực hiện chung và ràng buộc IRepository<>để GenericRepository<>trong thư viện IOC của bạn. Câu trả lời này rất cũ. Tôi nghĩ rằng giải pháp tốt nhất là kết hợp tất cả các kho lưu trữ trong một lớp được gọi UnitOfWork. Nó nên chứa kho lưu trữ của mọi loại và một phương thức được gọi SaveChanges. Tất cả các kho lưu trữ nên chia sẻ một bối cảnh EF.
LukLed

2
thay vì trả lại viewmodel từ lớp dịch vụ, bạn nên trả lại DTO và chuyển đổi nó thành viewModels bằng automapper .. đôi khi chúng giống nhau, khi chúng không biết ơn bạn đã thực hiện YGTNI "Bạn sẽ đến Cần nó "
hanzolo

41

Như Carnotaurus cho biết kho lưu trữ chịu trách nhiệm ánh xạ dữ liệu của bạn từ định dạng lưu trữ đến các đối tượng kinh doanh của bạn. Nó sẽ xử lý cả cách đọc và ghi dữ liệu (xóa, cập nhật quá) từ và vào bộ lưu trữ.

Mặt khác, mục đích của lớp dịch vụ là gói gọn logic kinh doanh vào một nơi duy nhất để thúc đẩy việc tái sử dụng mã và phân tách các mối quan tâm. Điều này thường có ý nghĩa gì đối với tôi trong thực tế khi xây dựng các trang web Asp.net MVC là tôi có cấu trúc này

[Trình điều khiển] gọi [Dịch vụ] người gọi [kho (ies)]

Một nguyên tắc tôi thấy hữu ích là giữ logic ở mức tối thiểu trong bộ điều khiển và kho lưu trữ.

Trong bộ điều khiển là bởi vì nó giúp giữ cho tôi KHÔ. Điều rất phổ biến là tôi cần sử dụng cùng một bộ lọc hoặc logic ở một nơi khác và nếu tôi đặt nó trong bộ điều khiển, tôi không thể sử dụng lại nó.

Trong kho lưu trữ là vì tôi muốn có thể thay thế bộ lưu trữ của mình (hoặc ORM) khi có thứ gì đó tốt hơn xuất hiện. Và nếu tôi có logic trong kho lưu trữ, tôi cần viết lại logic này khi tôi thay đổi kho lưu trữ. Nếu kho lưu trữ của tôi chỉ trả về IQueryable và mặt khác dịch vụ lọc, tôi sẽ chỉ cần thay thế ánh xạ.

Ví dụ, gần đây tôi đã thay thế một số kho lưu trữ Linq-To-Sql của tôi bằng EF4 và những kho lưu trữ theo nguyên tắc này có thể thay thế trong vài phút. Thay vào đó tôi có một số logic, đó chỉ là vấn đề hàng giờ.


Tôi đồng ý với bạn Mikael. Trong thực tế, tôi đã áp dụng kịch bản tương tự trong blog công nghệ freecodebase.com của tôi và tôi đã sử dụng mã tiếp cận đầu tiên trong triển khai này. Mã nguồn có thể được tải xuống ở đây là tốt.
Toffee

Tôi đã nghiên cứu chủ đề chung về việc áp dụng mẫu kho lưu trữ trong ứng dụng MVC hiện có. Nó là một khung bespoke với ORM giống như Bản ghi hoạt động và các quy ước Rails / Laravel khác và có một số vấn đề về kiến ​​trúc cho công việc tôi đang làm hiện tại. Một điều tôi đã gặp là các kho lưu trữ "không nên trả về ViewModels, DTO hoặc các đối tượng truy vấn", mà nên trả về các đối tượng kho lưu trữ. Tôi đang suy nghĩ về nơi các dịch vụ tương tác với các đối tượng kho lưu trữ thông qua các phương thức như onBeforeBuildBrowseQueryvà có thể sử dụng trình tạo truy vấn để thay đổi truy vấn.
thư pháp-io

@Toffee, liên kết của bạn bị hỏng, bạn có thể vui lòng cập nhật nó không, tôi cần mã nguồn để thực hiện.
Hamza Khanzada

24

Câu trả lời được chấp nhận (và được nâng cao hàng trăm lần) có một lỗ hổng lớn. Tôi muốn chỉ ra điều này trong bình luận nhưng nó sẽ chỉ bị chôn vùi ở đó trong 30 bình luận gì đó để chỉ ra ở đây.

Tôi đã tiếp nhận một ứng dụng doanh nghiệp được xây dựng theo cách đó và phản ứng ban đầu của tôi là WTH ? ViewModels trong lớp dịch vụ? Tôi không muốn thay đổi quy ước vì nhiều năm phát triển đã đi vào nó nên tôi tiếp tục trả lại ViewModels. Cậu bé đã biến thành cơn ác mộng khi chúng tôi bắt đầu sử dụng WPF. Chúng tôi (nhóm các nhà phát triển) luôn nói: ViewModel nào? Cái thực sự (cái chúng tôi đã viết cho WPF) hay cái dịch vụ? Chúng được viết cho một ứng dụng web và thậm chí có cờ IsReadOnly để tắt chỉnh sửa trong UI. Lỗi lớn, lỗ hổng lớn và tất cả chỉ vì một từ: ViewModel !!

Trước khi bạn mắc lỗi tương tự, đây là một số lý do khác ngoài câu chuyện của tôi ở trên:

Trả lại một ViewModel từ lớp dịch vụ là một điều không lớn. Điều đó giống như nói:

  1. Nếu bạn muốn sử dụng các dịch vụ này, tốt hơn bạn nên sử dụng MVVM và đây là ViewModel bạn cần sử dụng. Ôi!

  2. Các dịch vụ đang đưa ra giả định rằng chúng sẽ được hiển thị trong UI ở đâu đó. Điều gì nếu nó được sử dụng bởi một ứng dụng không phải UI như dịch vụ web hoặc dịch vụ windows?

  3. Đó thậm chí không phải là một ViewModel thực sự. Một ViewModel thực sự có khả năng quan sát, các lệnh, v.v ... Đó chỉ là một POCO có tên xấu. (Xem câu chuyện của tôi ở trên để biết tại sao tên quan trọng.)

  4. Ứng dụng tiêu thụ tốt hơn là một lớp trình bày (ViewModels được sử dụng bởi lớp này) và nó hiểu rõ hơn về C #. Một Ouch khác!

Làm ơn đừng làm vậy!


3
Tôi chỉ phải bình luận về điều này mặc dù tôi biết rằng nó không thêm vào cuộc thảo luận: "Đó chỉ là một POCO với một tên xấu." << - Trông thật đẹp trên áo phông! :) :)
Mephisztoe

8

Thông thường, một kho lưu trữ được sử dụng làm giàn giáo để đưa vào các thực thể của bạn - một lớp dịch vụ sẽ ra ngoài và cung cấp một yêu cầu. Có khả năng bạn sẽ đặt một kho lưu trữ dưới lớp dịch vụ của bạn.


Vì vậy, trong một ứng dụng ASP.NET MVC sử dụng EF4, có thể một cái gì đó như thế này: SQL Server -> EF4 -> Kho lưu trữ -> Lớp dịch vụ -> Mô hình -> Trình điều khiển và ngược lại?
Sam

1
Vâng, kho lưu trữ của bạn có thể được sử dụng để có được các thực thể nhẹ từ EF4; và lớp dịch vụ của bạn có thể được sử dụng để gửi lại cho người quản lý mô hình chuyên biệt (Mô hình trong kịch bản của bạn). Bộ điều khiển sẽ gọi xuống trình quản lý mô hình chuyên dụng của bạn để thực hiện việc này ... Hãy xem nhanh blog của tôi cho Mvc 2 / 3. Tôi có sơ đồ.
CarneyCode

Chỉ cần làm rõ: EF4 trong kịch bản của bạn là nơi Mô hình nằm trên sơ đồ của tôi và Mô hình trong kịch bản của bạn là các trình quản lý mô hình chuyên biệt trong sơ đồ của tôi
CarneyCode

4

Lớp lưu trữ được triển khai để truy cập cơ sở dữ liệu và giúp mở rộng các hoạt động CRUD trên cơ sở dữ liệu. Trong khi đó một lớp dịch vụ bao gồm logic nghiệp vụ của ứng dụng và có thể sử dụng lớp kho lưu trữ để triển khai logic nhất định liên quan đến cơ sở dữ liệu. Trong một ứng dụng, tốt hơn là có một lớp kho lưu trữ và lớp dịch vụ riêng biệt. Việc có các lớp dịch vụ và kho lưu trữ riêng biệt làm cho mã trở nên mô đun hơn và tách rời cơ sở dữ liệu khỏi logic nghiệp vụ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.