2009-07-17 12 views
10

Tôi xin lỗi trước nếu mọi người nghĩ rằng điều này đã bị đánh đến chết. Tôi đã chỉ dành vài giờ cuối cùng tìm kiếm và đọc nhiều bài viết tuyệt vời ở đây trong SO nhưng tôi vẫn còn bối rối.DDD, Repository & Encapsulation

Nguồn gây nhầm lẫn của tôi là DTO so với DDD và kho lưu trữ. Tôi muốn các đối tượng miền POCO của tôi có các đồ họa thông minh và tôi muốn lấy chúng từ các kho lưu trữ. Nhưng có vẻ như tôi phải vi phạm một số quy tắc đóng gói để thực hiện công việc đó, và có vẻ như nó có thể biến DTO lên đầu họ.

Dưới đây là một ví dụ đơn giản: Trong ứng dụng danh mục của chúng tôi, một phần có thể là một gói bao gồm một số phần khác. Vì vậy, nó có ý nghĩa đối với Part POCO để có một phương thức 'GetChildren()' trả về IEnumerable < Part>. Nó thậm chí có thể làm những thứ khác với danh sách trên đường ra.

Nhưng danh sách đó được giải quyết như thế nào? Có vẻ như kho là câu trả lời:

interface IPartRepository : IRepository<Part> 
{ 
    // Part LoadByID(int id); comes from IRepository<Part> 
    IEnumerable<Part> GetChildren(Part part); 
} 

class Part 
{ 
    ... 
    public IEnumerable<Part> GetChildren() 
    { 
     // Might manipulate this list on the way out! 
     return partRepository.GetChildren(this); 
    } 
} 

Bây giờ người tiêu dùng của các cửa hàng của tôi, ngoài việc (chính xác) phần tải từ kho, cũng có thể bỏ qua một số Part-đóng gói logic bằng cách gọi trực tiếp GetChildren (một phần). Điều đó có tệ không?

Tôi đọc các kho lưu trữ đó sẽ phân phát POCO nhưng DTO là tốt để chuyển dữ liệu 'giữa các lớp'. Rất nhiều thuộc tính phần được tính toán - ví dụ, được tính toán dựa trên các quy tắc định giá phức tạp. Một mức giá thậm chí sẽ không được trong một DTO đến từ một kho lưu trữ - vì vậy nó có vẻ như đi qua dữ liệu giá trở lại một dịch vụ web yêu cầu DTO để tiêu thụ một phần, không phải là cách khác xung quanh.

Quá trình này đã quá dài. Đầu của tôi bị tháo ra đâu?

Trả lời

2

Một cách tiếp cận giải quyết vấn đề này là di chuyển logic vào các phần con - nghĩa là thay đổi ngữ nghĩa của lớp sao cho các đối tượng Part chịu trách nhiệm tự biến đổi khi chúng được liên kết với một phụ huynh.

Ví dụ, nếu giá của một Part phụ thuộc vào mẹ Part, giá cả có thể được xác định tại những thời điểm sau (ở mức tối thiểu):

  • Sau khi xây dựng, nếu cha mẹ Part (và tất cả các dữ liệu cần thiết khác) có sẵn.

  • Trong phương thức AttachToParent(Part parentPart) hoặc để phản hồi sự kiện, tức là, OnAttachedToParent(Part parentPart).

  • Khi cần mã khách hàng (tức là, trên lần truy cập đầu tiên của thuộc tính Price).


Edit: câu trả lời ban đầu của tôi (dưới đây) thực sự không phải là theo tinh thần của DDD. Nó liên quan đến việc tạo ra các đối tượng miền container đơn giản, một thiết kế được nhiều người coi là một mẫu chống (xem Anemic Domain Model).

Một lớp bổ sung giữa PartIPartRepository (Tôi sẽ gọi nó IPartService) giải quyết vấn đề này: di chuyển GetChildren(part) vào IPartService và loại bỏ nó khỏi Part, sau đó làm cho mã khách hàng gọi IPartService để có được Part đối tượng và con cái của họ chứ không phải là nhấn trực tiếp kho lưu trữ. Lớp Part vẫn có thuộc tính ChildParts (hoặc Children) - nó không biết cách tự điền nó.

Rõ ràng, điều này áp đặt chi phí bổ sung - bạn có thể kết thúc bằng văn bản hoặc tạo nhiều mã thông qua cho các lệnh gọi kho nếu bạn không cần thêm logic nghiệp vụ trong hầu hết các trường hợp.

+0

Thú vị. Nhưng tôi bị nhầm lẫn bởi 'di chuyển GetChildren (một phần) vào IPartService và xóa nó khỏi phần' và sau đó 'Lớp Part vẫn có thuộc tính Childparts.' Điều gì sẽ xảy ra nếu một phần nhu cầu xoa bóp trẻ em vì một lý do nào đó? – n8wrl

+1

Nếu việc xoa bóp cần phải xảy ra ngay lập tức khi bạn lấy nó từ kho lưu trữ, tôi sẽ đặt logic đó trong 'IPartService.GetChildren()'. Nếu bạn cần để có thể sửa đổi các phần con tại thời điểm tùy ý, bạn có thể thực hiện một phương thức dịch vụ khác, chẳng hạn như 'IPartService.UpdateChildPartPrices (Part part) .' (Hoặc cả hai - bạn có thể gọi' UpdateChildPartPrices' từ 'GetChildren'.) –

+0

Cảm ơn bạn đã dành thời gian, Jeff! – n8wrl

0

Phần còn thiếu của phương trình ở đây là hành vi của đối tượng Parts và cách bạn muốn làm việc với tổng hợp. Bạn có cần phải làm việc trên từng trẻ em của mỗi Part để đệ quy thứ n, hay bạn chỉ làm việc với một "gốc" Part (tức là những người không có cha mẹ) và đó là toàn bộ trẻ em?

Có một gốc tổng hợp Part có chứa một danh sách các khá quát gõ Parts như những đứa trẻ có vẻ như nó sẽ không thể hiện mô hình tên miền của bạn đặc biệt tốt, nhưng bạn có thể làm điều đó và tải đệ quy lười biếng mỗi bộ sưu tập con. Tuy nhiên, tôi vẫn rất cẩn thận, nơi có thể đệ quy vô hạn.

Vì mối quan tâm thứ hai của bạn, DTO không chuyển dữ liệu giữa các lớp nhiều như chúng để chuyển dữ liệu vào và ra khỏi lớp ứng dụng của bạn.

Chúng rất hữu ích nếu bạn đang sử dụng kiến ​​trúc hướng dịch vụ (bạn đề cập đến các dịch vụ web, nhưng nó có thể là bất kỳ SOA nào). Các dịch vụ của bạn sẽ truy vấn các kho lưu trữ của bạn, thực hiện thêm bất kỳ công việc nào, sau đó ánh xạ các đối tượng miền của bạn vào các DTO phẳng để gửi lại cho các máy khách yêu cầu. DTO nên đơn giản, không chứa logic và chức năng ứng dụng cụ thể với ý định được sắp xếp theo thứ tự.

Sử dụng các đối tượng miền bên trong ứng dụng của bạn và DTO bên ngoài.