2010-06-08 8 views
53

Giả sử bạn có một dự án ASP.NET MVC và đang sử dụng một lớp dịch vụ, chẳng hạn như tiếp xúc này quản lý hướng dẫn trên trang web asp.net: http://www.asp.net/mvc/tutorials/iteration-4-make-the-application-loosely-coupled-csLớp dịch vụ có nên trả về các kiểu xem cho một ứng dụng MVC không?

Nếu bạn có viewmodels cho quan điểm của bạn, là lớp dịch vụ nơi thích hợp để cung cấp cho mỗi viewmodel? Ví dụ, trong mẫu mã lớp dịch vụ có một phương pháp

public IEnumerable<Contact> ListContacts() 
    { 
     return _repository.ListContacts(); 
    } 

Nếu thay vào đó bạn muốn có một IEnumerable, nó nên đi trong lớp dịch vụ, hoặc là có một nơi khác đó là "đúng" chỗ?

Có lẽ phù hợp hơn, nếu bạn có chế độ xem riêng biệt cho mỗi chế độ xem được liên kết với ContactController, ContactManagerService có phương thức riêng để trả về mỗi chế độ xem không? Nếu lớp dịch vụ không phải là nơi thích hợp, các đối tượng viewmodel sẽ được khởi tạo để sử dụng bởi bộ điều khiển ở đâu?

+0

Bạn cũng có thể muốn xem http://weblogs.asp.net/scottgu/archive/2009/04/28/free-asp-net-mvc-nerddinner-tutorial-now-in-html. aspx –

+3

Hướng dẫn NerdDinner là nơi tồi tệ nhất để tìm kiếm thông tin này. Thật tuyệt vời khi trình diễn các tính năng MVC nhưng kiến ​​trúc mà nó ngụ ý là khủng khiếp. – Aaronaught

+1

@Aaronaught vì bạn rõ ràng là một chuyên gia, đó là hướng dẫn chuyên sâu của bạn về kiến ​​trúc MVC chính xác ở đâu? –

Trả lời

37

Nói chung, không.

Xem mô hình nhằm cung cấp thông tin đến và từ lượt xem và phải cụ thể cho ứng dụng, chứ không phải với miền chung. Bộ điều khiển nên phối hợp tương tác với kho, dịch vụ (Tôi đang đưa ra một số giả định về định nghĩa dịch vụ ở đây), vv và xử lý việc xây dựng và xác nhận các mô hình khung nhìn, và cũng chứa logic xác định các khung nhìn để render.

Bằng cách làm rò rỉ mô hình xem thành lớp "dịch vụ", bạn đang làm mờ các lớp và hiện có thể ứng dụng và trình bày cụ thể được trộn lẫn với những gì nên tập trung với trách nhiệm cấp miền.

+0

Tom: Trong ví dụ, phương thức Index() của bộ điều khiển xây dựng khung nhìn nó trả về từ phương thức ListContacts() được hiển thị ở trên, trả về một IEnumberable , vì vậy không phải là viewmodel đã bị rò rỉ vào "lớp dịch vụ"? Đây có phải là một ví dụ nghèo nàn? Và nếu vậy, đâu là nơi thích hợp để khởi tạo các mô hình view cho tiêu thụ bởi các khung nhìn khác nhau của bộ điều khiển? – erg39

+0

@ erg39: Không liên hệ với loại mô hình miền? Nếu có, thì không. Bạn có thể có một cái gì đó trông giống như một liên hệ cho mô hình xem của bạn nhưng cũng có thể có thông tin bổ sung cụ thể cho một lần xem hoặc tập hợp các khung nhìn. Liên hệ không (hoặc không nên) biết bất cứ điều gì về bất kỳ chi tiết cụ thể cho một lần xem. – captaintom

+1

@ erg39: Theo như mô hình chế độ xem khởi tạo, bộ điều khiển có trách nhiệm xây dựng các trường hợp mô hình chế độ xem mới và xác thực/xác thực các mô hình xem nhận được từ chế độ xem. – captaintom

5

Tôi cho rằng điều đó phụ thuộc vào những gì bạn xem là "dịch vụ". Tôi chưa bao giờ thực sự thích cụm từ dịch vụ trong ngữ cảnh của một lớp đơn lẻ; nó vô cùng mơ hồ và không cho bạn biết nhiều về mục đích thực sự của lớp.

Nếu "lớp dịch vụ" là một lớp vật lý, chẳng hạn như dịch vụ web, thì tuyệt đối không; các dịch vụ trong ngữ cảnh SOA nên trưng ra các hoạt động miền/doanh nghiệp, không phải dữ liệu và không phải logic trình bày. Nhưng nếu dịch vụ chỉ được sử dụng như một khái niệm trừu tượng cho một mức độ đóng gói cao hơn, tôi không thấy bất kỳ vấn đề nào khi sử dụng nó theo cách bạn mô tả.

Chỉ cần không trộn lẫn các khái niệm. Nếu dịch vụ của bạn thỏa thuận với các mô hình xem thì đó phải là dịch vụ trình bày và được xếp chồng lên trên mô hình thực tế, không bao giờ chạm trực tiếp vào cơ sở dữ liệu hoặc bất kỳ logic nghiệp vụ nào.

+0

Trong mẫu, "lớp dịch vụ" là một sự trừu tượng giữa bộ điều khiển và một tầng kho lưu trữ có mục đích đã nêu sẽ được sử dụng để xác thực trước khi chuyển dữ liệu vào kho lưu trữ. Nó cũng chứa phương thức ListContacts() ở trên cũng như phương thức GetContact() để lấy một đối tượng Contact duy nhất được bộ điều khiển sử dụng cho các khung nhìn ràng buộc. Vì vậy, nó có vẻ như nó có vẻ thích hợp cho lớp dịch vụ cho biết để biết về danh sách các viewmodels có thể được cần thiết bởi một bộ điều khiển hoàn toàn fleshed ra, chính xác? – erg39

+0

@ erg39: Nếu dịch vụ của bạn đang xác thực thì về cơ bản nó là một phần của mô hình. Nó không xử lý logic trình bày và không được kết hợp với các mô hình xem. – Aaronaught

+0

@Aaronaught: Bạn sẽ nói nơi thích hợp để khởi tạo chế độ xem sẽ là gì? Nếu nó chỉ là một trừu tượng riêng biệt, giống như một "lớp dịch vụ viewmodel"? – erg39

21

Không, tôi không nghĩ vậy. Các dịch vụ chỉ nên quan tâm đến miền có vấn đề, không phải là chế độ xem hiển thị kết quả. Giá trị trả về phải được thể hiện dưới dạng đối tượng miền, chứ không phải dạng xem.

16

Theo cách tiếp cận truyền thống hoặc lý thuyết khôn ngoan, ViewModel phải là một phần của lớp giao diện người dùng. Ít nhất là cái tên nói như vậy.

Nhưng khi bạn bắt đầu tự thực hiện nó với Khung thực thể, MVC, Kho lưu trữ, v.v, thì bạn sẽ nhận ra điều gì đó khác.

Ai đó phải lập bản đồ Mô hình thực thể/DB với Chế độ xem (DTO được đề cập ở cuối). Điều này có nên được thực hiện trong [A] lớp giao diện người dùng (bởi Bộ điều khiển) hay trong [B] lớp Dịch vụ?

Tôi đi với Tùy chọn B.Tùy chọn A là không không vì thực tế đơn giản là một số mô hình thực thể kết hợp với nhau để tạo thành một ViewModel. Chúng ta không thể truyền dữ liệu không cần thiết cho lớp UI, trong khi trong tùy chọn B, dịch vụ có thể chơi với dữ liệu và chỉ truyền yêu cầu/tối thiểu đến lớp UI sau khi ánh xạ (tới ViewModel).

Một lần nữa, chúng ta hãy đi với tùy chọn A, đặt ViewModel trong lớp giao diện người dùng (và mô hình thực thể trong lớp dịch vụ).

Nếu lớp dịch vụ cần ánh xạ tới ViewModel, thì lớp Dịch vụ cần truy cập ViewModel trong lớp giao diện người dùng. Thư viện/dự án nào? ViewModel phải nằm trong một dự án riêng biệt trong lớp giao diện người dùng và dự án này cần được tham chiếu bởi Lớp dịch vụ. Nếu ViewModel không nằm trong một dự án riêng biệt, thì có tham chiếu vòng tròn, vì vậy không đi. Có vẻ khó xử khi có lớp UI truy cập lớp dịch vụ nhưng chúng tôi vẫn có thể đối phó với nó.

Nhưng điều gì xảy ra nếu có một ứng dụng giao diện người dùng khác sử dụng dịch vụ này? Điều gì sẽ xảy ra nếu có ứng dụng dành cho thiết bị di động? ViewModel có thể khác nhau như thế nào? Dịch vụ có nên truy cập vào cùng một dự án mô hình xem không? Tất cả các dự án giao diện người dùng có truy cập vào cùng một dự án ViewModel hay không?

Sau những cân nhắc này, câu trả lời của tôi là đặt dự án ViewModel trong Lớp dịch vụ. Mỗi lớp giao diện người dùng phải truy cập vào lớp Dịch vụ. Và có thể có rất nhiều ViewModels tương tự mà tất cả chúng có thể sử dụng (do đó ánh xạ trở nên dễ dàng hơn cho lớp dịch vụ). Ánh xạ được thực hiện thông qua LINQ những ngày này, đó là một cộng thêm.

Cuối cùng, có cuộc thảo luận này về DTO. Và cũng về chú thích dữ liệu trong ViewModels. ViewModels với chú thích dữ liệu (Microsoft.Web.Mvc.DataAnnotations.dll) không thể cư trú trong lớp dịch vụ thay vì chúng cư trú trong lớp giao diện người dùng (nhưng ComponentModel.DataAnnotations.dll có thể nằm trong lớp dịch vụ). Vì vậy, DTO thực sự là một ViewModel bởi vì chủ yếu sẽ có một trên một bản đồ giữa hai (nói với AutoMapper). Một lần nữa DTO vẫn có logic cần thiết cho UI (hoặc nhiều ứng dụng) và nằm trong Service Layer. Và lớp UI ViewModel (nếu chúng ta sử dụng Microsoft.Web.Mvc.DataAnnotations.dll) chỉ để sao chép dữ liệu từ DTO, với một số 'hành vi'/thuộc tính được thêm vào nó.

[Bây giờ cuộc thảo luận này là về để có một lượt thú vị hãy đọc tiếp ...: I]

Và đừng nghĩ thuộc tính dữ liệu chú thích là chỉ dành riêng cho giao diện người dùng. Nếu bạn giới hạn xác thực bằng cách sử dụng System.ComponentModel.DataAnnotations.dll thì cùng một viewmodel cũng có thể được sử dụng cho front232 end & xác nhận hợp lệ phụ trợ (do đó loại bỏ UI-residing-viewmodel-copy-of-DTO). Nhiều hơn các thuộc tính cũng có thể được sử dụng trong các mô hình thực thể. Ví dụ: sử dụng .tt, các khung dữ liệu của Entity Framework có thể được tạo tự động với các thuộc tính xác thực để thực hiện một số xác nhận hợp lệ db như độ dài tối đa trước khi gửi đến phần cuối. Một ưu điểm nữa là nếu các thay đổi hợp lệ của backend trong DB thì .tt (đọc db specifics và tạo thuộc tính cho lớp thực thể) sẽ tự động chọn nó. Điều này có thể buộc thử nghiệm đơn vị xác thực UI thất bại, đó là một dấu cộng lớn (vì vậy chúng tôi có thể sửa và thông báo cho tất cả UI/người tiêu dùng thay vì vô tình quên và thất bại). Vâng, cuộc thảo luận đang hướng tới một thiết kế khung tốt. Như bạn có thể thấy, tất cả đều liên quan: xác thực cấp độ khôn ngoan, chiến lược kiểm tra đơn vị, chiến lược bộ nhớ đệm, v.v.

Mặc dù không liên quan trực tiếp đến câu hỏi. 'ViewModel Façade' được đề cập trong này phải xem channel 9 link cũng đáng để khám phá. Nó bắt đầu chính xác ở 11 phút 49 giây trong video. Bởi vì đây sẽ là bước tiếp theo/suy nghĩ khi câu hỏi hiện tại của bạn ở trên được sắp xếp: 'Cách tổ chức chế độ xem?'

Cũng trong ví dụ "_repository.ListContacts()" của bạn đang trả về chế độ xem từ kho lưu trữ. Đây không phải là cách trưởng thành. Các kho lưu trữ nên cung cấp các mô hình thực thể hoặc các mô hình db. Điều này được chuyển đổi thành các mô hình xem và đó là mô hình khung nhìn này được lớp dịch vụ trả về.

+0

Isnt giới thiệu một lớp Ứng dụng di chuyển chính xác ở đây? nó sẽ ánh xạ các mô hình khung nhìn và các thực thể miền, giữ cho nhau độc lập và hạnh phúc. – sergio

+0

Chế độ xem và các thực thể tên miền là anyways độc lập. Là "lớp ứng dụng" một lớp mới chỉ để ánh xạ các thực thể? bạn có nghĩ về lớp UI và dịch vụ truy cập lớp ứng dụng này không? bởi vì cuối cùng họ vẫn cần truy cập vào các mô hình của họ. Hoặc trong các từ khác, một lớp bổ sung sẽ giải quyết vấn đề gì? –

4

Điều này đã đến một chút "nó phụ thuộc" nơi tôi làm việc - chúng tôi thường có một bộ điều khiển tiêu thụ một số dịch vụ - sau đó kết hợp DTO trả về thành 'ViewModel' và sau đó sẽ được truyền cho khách hàng - hoặc thông qua kết quả JSON hoặc bị ràng buộc trong Mẫu dao cạo.

Thing, khoảng 80% thời gian - ánh xạ của DTO tới ViewModel là 1-1. Chúng tôi đang bắt đầu di chuyển về phía 'Khi cần thiết, chỉ cần tiêu thụ trực tiếp DTO, nhưng khi DTO và những gì chúng tôi cần trong ứng dụng/chế độ xem của chúng tôi không khớp - thì chúng tôi tạo ViewModel và thực hiện ánh xạ giữa các đối tượng khi cần'.

Mặc dù tôi vẫn chưa tin rằng đây là giải pháp tốt nhất hoặc đúng - vì nó kết thúc bằng việc thảo luận nóng 'chúng ta chỉ thêm X vào DTO để đáp ứng nhu cầu của chế độ xem?'

+1

Nếu bạn giữ DTO trong lớp dịch vụ và sử dụng nó như một ViewModel trong giao diện người dùng, nơi bạn đặt xác nhận (DataAnnotation, FluentValidation hoặc cái gì khác)? Tôi đang ở trong tình trạng tương tự của "80% 1: 1 DTO để VM" vì vậy không chắc chắn những gì tôi nên làm. – Iztoksson