Các phương pháp hay nhất để ánh xạ DTO với đối tượng miền?


81

Tôi đã thấy rất nhiều câu hỏi liên quan đến ánh xạ DTO với các đối tượng miền, nhưng tôi không cảm thấy họ trả lời câu hỏi của tôi. Tôi đã sử dụng nhiều phương pháp trước đây và có ý kiến ​​của riêng mình nhưng tôi đang tìm kiếm thứ gì đó cụ thể hơn một chút.

Tình huống:

Chúng tôi có nhiều đối tượng miền. Chúng tôi đang sử dụng mô hình CSLA nên các đối tượng miền của chúng tôi có thể khá phức tạp và chúng chứa quyền truy cập dữ liệu của riêng chúng. Bạn không muốn chuyển những thứ này xung quanh trên dây. Chúng tôi sẽ viết một số dịch vụ mới sẽ trả về dữ liệu ở một số định dạng (.Net, JSON, v.v.). Vì lý do này (và các lý do khác), chúng tôi cũng đang tạo một đối tượng truyền dữ liệu tinh gọn để truyền trên dây.

Câu hỏi của tôi là: Đối tượng DTO và Domain nên được kết nối như thế nào?

Phản ứng đầu tiên của tôi là sử dụng dung dịch kiểu mẫu DTO Fowler . Tôi đã thấy điều này được thực hiện nhiều lần và nó cảm thấy phù hợp với tôi. Đối tượng miền không chứa tham chiếu đến DTO. Một thực thể bên ngoài ("người lập bản đồ" hoặc "trình hợp dịch") được gọi để tạo DTO từ Đối tượng miền. Thông thường có một ORM ở phía đối tượng miền. Nhược điểm của điều này là "người vẽ bản đồ" có xu hướng trở nên cực kỳ phức tạp đối với bất kỳ tình huống thực tế nào và có thể rất mong manh.

Một ý tưởng khác được đưa ra là để Đối tượng miền "chứa" DTO, vì nó chỉ là một đối tượng dữ liệu đơn giản. Thuộc tính Đối tượng miền sẽ tham chiếu nội bộ các thuộc tính DTO và chỉ có thể trả về DTO nếu được yêu cầu. Tôi có thể thấy không có vấn đề với điều này nhưng nó cảm thấy sai. Tôi đã thấy một số bài báo mà những người sử dụng NHibernate dường như sử dụng phương pháp này.

Có những cách khác? Một trong những cách trên có đáng sử dụng không? Nếu có hoặc nếu không, tại sao?


4
Máy tự động trông thật thú vị. Tôi đã thấy nhiều mã trước đó mà nó sẽ thay thế. Vấn đề chính của tôi ở đó là nếu tôi bị mắc kẹt với hàng tấn mã ánh xạ vì bất kỳ lý do gì, tôi muốn tự mình kiểm soát nó.
Brian Ellis,

2
Khi chúng tôi đi từ DTO đến Đối tượng miền, việc ánh xạ đó là thủ công 100%. Đó là một vấn đề khó giải quyết hơn nhiều, vì chúng tôi cố gắng giữ cho các đối tượng miền của mình dựa trên hoạt động, thay vì chỉ đơn thuần là vùng chứa dữ liệu. Đi đến một DTO, đó là một vấn đề dễ giải quyết.
Jimmy Bogard

Một tùy chọn khác là phiên bản beta của ServiceToolkit.NET, mà chúng tôi đã bắt đầu trong dự án cuối cùng của mình. Có thể nó có thể giúp bạn: http://servicetoolkit.codeplex.com/

Tôi đồng ý rằng sai ở chỗ đối tượng miền không được biết về đối tượng dto. Mặc dù chúng có thể có liên quan trong trường hợp này, nhưng mục đích của chúng hoàn toàn riêng biệt (dtos thường được tạo ra cho mục đích) và bạn sẽ tạo ra một sự phụ thuộc không cần thiết.
Sinaesthetic

Câu trả lời:


40

Lợi ích của việc có một trình ánh xạ nằm giữa miền của bạn và DTO của bạn không xuất hiện khi bạn chỉ hỗ trợ một ánh xạ duy nhất, nhưng khi số lượng ánh xạ tăng lên, việc tách mã đó khỏi miền sẽ giúp giữ cho miền đơn giản và gọn gàng hơn. Bạn sẽ không làm lộn xộn miền của mình với quá nhiều trọng lượng.

Cá nhân tôi cố gắng giữ ánh xạ ngoài các thực thể miền của mình và đặt trách nhiệm vào cái mà tôi gọi là "Lớp quản lý / Dịch vụ". Đây là lớp nằm giữa ứng dụng và (các) kho lưu trữ và cung cấp logic nghiệp vụ như điều phối quy trình làm việc (Nếu bạn sửa đổi A, bạn cũng có thể phải sửa đổi B để dịch vụ A sẽ hoạt động với Dịch vụ B).

Nếu tôi có nhiều định dạng kết thúc có thể xảy ra, tôi có thể xem xét việc tạo một trình định dạng có thể cắm thêm có thể sử dụng mẫu Khách truy cập, chẳng hạn như để chuyển đổi các thực thể của tôi, nhưng tôi chưa thấy cần thiết cho bất kỳ thứ gì phức tạp này.


"(Nếu bạn sửa đổi A, bạn cũng có thể phải sửa đổi B để dịch vụ A sẽ hoạt động với Dịch vụ B)" - Điều này không theo logic kinh doanh sao? Tôi nghĩ rằng phần này nên đi đến quyền điều khiển hơn là dịch vụ?
Ayyappa

24

Bạn có thể sử dụng một trình tự động hóa, chẳng hạn như một trình tự động được viết bởi Jimmy Bogard không có kết nối giữa các đối tượng và dựa vào các quy ước đặt tên được tuân thủ.


9
Automapper có thể dẫn đến các thuộc tính vô tình bị lộ -> lỗ hổng bảo mật. Sẽ tốt hơn nếu nói rõ ràng những gì nên được tiếp xúc với tư cách là một DTO.
deamon

4
@deamon: mối quan tâm hợp lệ, nhưng các lỗi (và các lỗ hổng bảo mật tiềm ẩn do con người giám sát) có thể được tạo ra bằng cách viết tất cả mã ánh xạ gooey đó. Tôi sẽ đi con đường tự động và xử lý 5% bằng cách sử dụng tính năng lập bản đồ tùy chỉnh được tích hợp sẵn.
Merritt

@deamon - bạn không thể thực hiện ánh xạ có điều kiện cho những thuộc tính mà bạn không nên để lộ? Suy nghĩ AutoMapper xử lý tình huống đó?
Richard B

Nếu bạn sử dụng AutoMapper, tôi nghĩ điều cực kỳ quan trọng là bạn phải có tất cả các bài kiểm tra đơn vị để kiểm tra xem việc ánh xạ có được thực hiện chính xác hay không.
L-Four

7

Chúng tôi sử dụng các mẫu T4 để tạo các lớp ánh xạ.

Pro's - mã có thể đọc được của con người có sẵn tại thời điểm biên dịch, nhanh hơn trình lập bản đồ thời gian chạy. Kiểm soát 100% mã (có thể sử dụng một phần các phương pháp / mẫu mẫu để mở rộng chức năng trên cơ sở đặc biệt)

Con's - loại trừ một số thuộc tính, tập hợp các đối tượng miền, v.v., học cú pháp T4.


6

Giữ logic ánh xạ bên trong thực thể của bạn có nghĩa là Đối tượng miền của bạn giờ đây đã biết về "chi tiết triển khai" mà nó không cần biết. Nói chung, DTO là cửa ngõ của bạn với thế giới bên ngoài (từ một yêu cầu đến hoặc thông qua việc đọc từ một dịch vụ / cơ sở dữ liệu bên ngoài). Vì thực thể là một phần của Logic kinh doanh của bạn, có lẽ tốt nhất bạn nên giữ những chi tiết đó bên ngoài thực thể.

Giữ bản đồ ở một nơi khác sẽ là giải pháp thay thế duy nhất - nhưng nó nên đi đâu? Tôi đã thử giới thiệu các đối tượng / dịch vụ ánh xạ nhưng, sau khi tất cả đã được nói và thực hiện, có vẻ như khai thác quá mức (và có thể là như vậy). Tôi đã có một số thành công khi sử dụng Automapper và những thứ như vậy cho các dự án nhỏ hơn nhưng các công cụ như Automapper đi kèm với những cạm bẫy của riêng chúng. Tôi đã gặp một số vấn đề khá khó khăn để tìm ra các vấn đề liên quan đến ánh xạ vì ánh xạ của Automapper là ẩn và hoàn toàn tách rời khỏi phần còn lại của mã của bạn (không giống như "tách các mối quan tâm" mà giống như "ánh xạ của Godforsaken sống ở đâu") vì vậy chúng đôi khi có thể khó theo dõi. Không có nghĩa là Automapper không có công dụng của nó, bởi vì nó có. Tôi chỉ nghĩ rằng việc lập bản đồ phải là thứ rõ ràng và minh bạch nhất có thể để tránh các vấn đề.

Thay vì tạo một lớp dịch vụ ánh xạ, tôi đã thành công trong việc giữ các ánh xạ bên trong DTO của mình. Vì DTO luôn nằm ở ranh giới của ứng dụng, chúng có thể được biết đến về Đối tượng kinh doanh và tìm ra cách ánh xạ từ / đến chúng. Ngay cả khi số lượng ánh xạ mở rộng đến một lượng hợp lý, nó vẫn hoạt động rõ ràng. Tất cả các ánh xạ ở cùng một nơi và bạn không phải quản lý một loạt các dịch vụ ánh xạ bên trong Lớp dữ liệu, Lớp chống gián đoạn hoặc Lớp trình bày của mình. Thay vào đó, ánh xạ chỉ là một chi tiết triển khai được ủy quyền cho DTO liên quan đến yêu cầu / phản hồi. Vì các bộ tuần tự hóa thường chỉ tuần tự hóa các thuộc tính và trường khi bạn gửi nó qua đường dây, bạn sẽ không gặp phải bất kỳ vấn đề nào. Cá nhân tôi thấy đây là lựa chọn sạch sẽ nhất và tôi có thể nói, theo kinh nghiệm của mình,


3

Bạn thấy như thế nào để triển khai một phương thức khởi tạo bên trong lớp DTO nhận làm tham số cho một đối tượng miền?

Nói ... Chuyện như thế này

class DTO {

     // attributes 

     public DTO (DomainObject domainObject) {
          this.prop = domainObject.getProp();
     }

     // methods
}

9
Xin đừng bao giờ làm điều này. Bạn không muốn lớp DTO của mình biết hoặc phụ thuộc vào lớp miền của bạn. Ưu điểm của ánh xạ là các lớp thấp hơn có thể dễ dàng chuyển ra ngoài bằng cách thay đổi ánh xạ, hoặc các sửa đổi ở lớp dưới có thể là bộ điều khiển bằng cách thay đổi ánh xạ. Giả sử dtoA ánh xạ tới domainObjectA ngày hôm nay, nhưng ngày mai yêu cầu là nó ánh xạ tới domainObjectB. Trong trường hợp của bạn, bạn phải sửa đổi đối tượng DTO, đây là một điều tối kỵ. Bạn đã mất rất nhiều lợi ích của người lập bản đồ.
Frederik Prijck,

2
Trước hết, cảm ơn! : D. Vì vậy, @FrederikPrijck bằng cách chèn một lớp giữa DTODomainObject, về cơ bản chúng tôi giải quyết vấn đề này của DTO phụ thuộc vào đối tượng miền, do đó, tất cả "công việc xây dựng" được thực hiện trong một lớp giữa (lớp) được gọi mapper, điều đó phụ thuộc vào cả hai DTO và DomainObjects. Vì vậy, đó là tốt nhất, hoặc nói chung là khuyến nghị, tiếp cận vấn đề này? Tôi chỉ yêu cầu để đảm bảo rằng điểm được hiểu.
Victor

4
Đúng, lớp được gọi là "Assembler". Bằng cách sử dụng lớp thứ 3 để xác định ánh xạ, bạn cho phép khả năng dễ dàng thay thế lớp trình hợp dịch bằng một cách triển khai khác (ví dụ: loại bỏ Automapper và sử dụng ánh xạ thủ công), đây luôn là lựa chọn tốt hơn. Cách tốt nhất để hiểu nó là nghĩ về nơi tôi sẽ cung cấp cho bạn Đối tượng A và ai đó khác cung cấp cho bạn Đối tượng B. Bạn không có quyền truy cập vào từng đối tượng đó (chỉ dll), vì vậy ánh xạ chỉ có thể được thực hiện bằng cách tạo thứ 3 lớp. Nhưng ngay cả khi bạn có thể truy cập bất kỳ đối tượng nào, các ánh xạ phải luôn được thực hiện bên ngoài, vì chúng không liên quan.
Frederik Prijck

1
Nhưng câu trả lời này trên thực tế là "nhiều hơn hữu ích" với các nhận xét và sửa chữa, nó mang lại cho bất kỳ người đọc nào thừa nhận và lời khuyên về vấn đề .. nó thực sự đóng góp để học hỏi, tôi không hiểu tại sao phải donwvote .. nó giúp tôi .. nhưng tôi không muốn bắt đầu thảo luận về điều đó .. tùy thuộc vào bạn. Dù sao cảm ơn vì câu trả lời.
Victor

3
Trên thực tế, tôi thích cách tiếp cận này, hiện tại tôi sử dụng hàm tạo để ánh xạ thực thể với DTO và sử dụng một lớp ánh xạ để ánh xạ đầu vào dto tới thực thể.
dream83619

1

Một giải pháp khả thi khác: http://glue.codeplex.com .

Đặc trưng:

  • Ánh xạ hai chiều
  • Ánh xạ tự động
  • Ánh xạ giữa các loại khác nhau
  • Lập bản đồ lồng nhau và Làm phẳng
  • Danh sách và Mảng
  • Xác minh các mối quan hệ
  • Kiểm tra ánh xạ
  • Thuộc tính, trường và phương thức


0

Tôi có thể đề xuất một công cụ tôi đã tạo và là mã nguồn mở được lưu trữ tại CodePlex: EntitiesToDTOs .

Ánh xạ từ DTO đến Thực thể và ngược lại được thực hiện bằng các phương pháp mở rộng, các phương pháp này tạo thành bên Bộ lắp ráp của mỗi đầu.

Bạn kết thúc bằng mã như:

Foo entity = new Foo();
FooDTO dto = entity.ToDTO();
entity = dto.ToEntity();

List<Foo> entityList = new List<Foo>();
List<FooDTO> dtoList = entityList.ToDTOs();
entityList = dtoList.ToEntities();

điều này là sai về mặt kiến ​​trúc vì bạn làm cho DTO và các thực thể miền nhận biết nhau.
Raffaeu

5
@Raffaeu Tôi không nghĩ vậy vì các phương thức ToDTO / ToDTOs / ToEntity / ToEntities được định nghĩa là các phương thức mở rộng đại diện cho Bộ lắp ráp. Logic của việc chuyển đổi Thực thể thành DTO và ngược lại là trong các phương thức mở rộng (Bộ lắp ráp), không phải trong Thực thể / DTO.
kzfabi 11/12/12

2
Nếu bạn nói về "Assembler", thì hãy triển khai chúng theo cách chính xác. Làm cho chúng theo mô-đun, làm cho chúng có thể thay đổi dễ dàng, sử dụng tiêm phụ thuộc. Bản thân mô hình miền không cần phải biết về việc chuyển đổi sang DTO. Giả sử tôi có 1 đối tượng miền nhưng 50 ứng dụng khác nhau sử dụng cùng một miền, mỗi ứng dụng có DTO riêng. Bạn sẽ không tạo 50 tiện ích mở rộng. Thay vào đó, bạn sẽ tạo một dịch vụ ứng dụng cho mỗi ứng dụng với (các) trình hợp dịch cần thiết được đưa vào như một phần phụ thuộc vào dịch vụ.
Frederik Prijck,

0

Tại sao chúng ta không thể làm như thế này?

class UserDTO {
}

class AdminDTO {
}

class DomainObject {

 // attributes
 public DomainObject(DTO dto) {
      this.dto = dto;
 }     

 // methods
 public function isActive() {
      return (this.dto.getStatus() == 'ACTIVE')
 }

 public function isModeratorAdmin() {
      return (this.dto.getAdminRole() == 'moderator')
 }

}


userdto = new UserDTO();
userdto.setStatus('ACTIVE');

obj = new DomainObject(userdto)
if(obj.isActive()) {
   //print active
}

admindto = new AdminDTO();
admindto.setAdminRole('moderator');

obj = new DomainObject(admindto)
if(obj.isModeratorAdmin()) {
   //print some thing
}

@FrederikPrijck (hoặc) ai đó: Vui lòng đề xuất. Trong ví dụ trên, DomainObject phụ thuộc vào DTO. Bằng cách này, tôi có thể tránh mã để ánh xạ đối tượng dto <--> domainobject.

hoặc lớp DomainObject có thể mở rộng lớp DTO?


0

Một tùy chọn khác sẽ là sử dụng ModelProjector . Nó hỗ trợ tất cả các tình huống có thể xảy ra và rất dễ sử dụng với diện tích tối thiểu.


0

Chúng ta có thể sử dụng mẫu Factory, Memento và Builder cho việc đó. Nhà máy ẩn các chi tiết về cách tạo phiên bản của mô hình miền từ DTO. Memento sẽ đảm nhận việc tuần tự hóa / giải mã hóa mô hình miền đến / từ DTO và thậm chí có thể truy cập các thành viên riêng tư. Builder sẽ cho phép ánh xạ từ DTO sang miền với giao diện thông thạo.

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.