6

Tôi đã đọc cuốn sách của Evans về DDD và đang nghĩ về cách nên triển khai tập hợp trong .NET. Hiện tại, tôi chỉ có thể nghĩ ra một cách; cô lập các tập hợp trong các thư viện lớp riêng biệt. Điều này, tuy nhiên, có vẻ như một chút overkill (tôi muốn giữ tất cả các đối tượng miền trong một thư viện) và tôi tự hỏi nếu có một cách khác nhau?DDD/Tổng hợp trong .NET

Lý do cho 1 lib/aggregate như sau: Gốc tổng hợp cần phải nhận thức được tất cả quyền truy cập vào 'các đối tượng con' mà nó chịu trách nhiệm, còn tổng hợp gốc có thể trả về các đối tượng con dưới dạng kết quả của nó các thành viên. Vì vậy, các thành viên (cần thiết bởi tổng hợp gốc) của các đối tượng phụ không thể được công khai. Vì vậy, lựa chọn duy nhất của bạn là làm cho chúng trở nên nội bộ (vì chúng vẫn cần được gọi bởi gốc tổng hợp). Bằng cách đặt tất cả các tập hợp trong một dự án, tuy nhiên, vẫn có thể truy cập các thành viên này từ các đối tượng miền khác đã thu được đối tượng phụ. Điều này là không mong muốn bởi vì nó cho phép người ta bỏ qua gốc tổng hợp. Bằng cách tách tất cả các tập hợp trong các thư viện khác nhau, vấn đề này được giải quyết.

Một số thông tin bổ sung:

Tôi đã kiểm tra ra DDD java sample code và họ đóng gói hàng tổng hợp (bao gồm các lớp học tất cả các tiểu đối tượng) trong một gói khác nhau. Các thành viên chỉ có thể được gọi từ gốc tổng hợp không có công cụ sửa đổi truy cập (ví dụ: Delivery.updateOnRouting). Trong java, các thành viên không có công cụ sửa đổi truy cập là package-private (chỉ có sẵn từ cùng một gói). Vì vậy, đây sẽ là hành vi chính xác.

Tuy nhiên, .NET sample code đặt tất cả các đối tượng miền trong một thư viện lớp, sau đó đặt thành viên tương ứng ở chế độ công khai. Đối với tôi, điều này có vẻ không chính xác.

+0

Bạn có thể giải thích ý bạn bằng "đối tượng phụ" không? Ý bạn là việc triển khai cụ thể các thực thể của bạn? Bạn có xem xét các lớp lồng nhau riêng để đạt được sự tách biệt sạch không? –

+0

+1, Thú vị, nhưng câu hỏi không rõ ràng ngay với tôi khi tôi đọc nó. Bạn có thể thuật lại nó một chút và thay đổi tiêu đề không? Đây là câu hỏi tôi đã chưng cất: Làm thế nào để tôi có được sự tách biệt rõ ràng giữa các rễ tổng hợp trong cùng một assembly .NET? – Marijn

Trả lời

2

Tôi chỉ có thể tìm ra một cách; cách ly các tập hợp trong các thư viện lớp riêng biệt . Này, tuy nhiên, có vẻ như một chút overkill

Giống như một nhiều overkill. Chi phí của việc làm như thế này sẽ là tàn bạo, bạn làm không muốn hàng chục khi hàng chục dự án, mà phương pháp này tạo ra trong bất kỳ ứng dụng không tầm thường nào.

+1

Chi phí chính xác mà bạn đang đề cập đến là gì? Bên cạnh đó là một chút điên rồ hành chính, ở đó tôi không thể thực sự thấy một lý do kỹ thuật để không làm điều này? – Freek

+1

@Lạch: bình luận tốt. Tại sao điều này không thể thực hiện được? Theo kinh nghiệm của tôi, nó là không thực tế khi làm việc với các giải pháp lớn với nhiều dự án trong Visual Studio, vì vậy bạn ít nhất có thể muốn tránh điều đó. – Marijn

+0

@Freek: Nó sẽ mang lại cho Visual Studio đầu gối của nó, thời gian biên dịch sẽ phát triển theo cấp số nhân và thậm chí khả năng sử dụng của VS có thể bị ảnh hưởng do độ trễ. Một dự án cho mỗi tổng hợp? Đó có thể là hàng trăm * dự án trong một số hệ thống và hàng tá ở bất kỳ hệ thống nào nhưng tầm thường nhất. –

2
Therefore, members (needed by the aggregate root) of these sub-objects can't be made public. 

Tôi cho rằng kết luận này quá cứng nhắc để thực tế và không phải là điều mà Evans đang ủng hộ. Có, anh ta không nói rằng Gốc Agg chịu trách nhiệm tạo và truy cập các đối tượng khác trong Root đó, NHƯNG

Thứ nhất, rễ tổng hợp có xu hướng trùng lặp. Nếu một widget được yêu cầu trong hai gốc khác nhau, vì lý do gì, nó là bắt buộc. Vì vậy, bạn thực sự không thể chỉ làm cho nó (các widget, trong trường hợp này) có sẵn cho một gốc và không phải là khác. Thứ hai, tôi nghĩ (giải thích của tôi và tôi không có cuốn sách ra!) Ý tưởng về một Agg Root với các khía cạnh để truy cập đối tượng là một trong những quy ước hơn là giáo điều. Giáo điều là để đáp ứng các yêu cầu của khách hàng trong bối cảnh của thư mục gốc đó để đơn giản hóa miền. Vấn đề của nó là phát triển một giao diện cho Root tổng hợp, và sau đó có khách hàng đi qua giao diện đó để đáp ứng nhu cầu của họ. Nếu bạn có thể hạn chế quyền truy cập vào các đối tượng mà (bất kỳ) khách hàng nào không cần (bằng cách sử dụng bất kỳ Tổng hợp gốc), bởi tất cả các phương tiện làm như vậy.

HTH,
Berryl

+0

Thực sự nó không phải là một quy ước, nhưng cần phải giữ cho các bất biến của tổng thể thỏa mãn. Ví dụ: nếu bạn có giới hạn về số tiền giá của một Đơn đặt hàng, bạn chỉ có thể cập nhật số lượng OrderLines thông qua Lệnh. Tuy nhiên, một khách hàng được phép xem nội dung của OrderLines. Điều này đòi hỏi rằng Order có thể cập nhật số lượng của OrderLine, nhưng các đối tượng khác thì không thể. Tôi không nghĩ rằng các đối tượng trong tổng hợp được chia sẻ với các tập hợp khác, nhưng tôi không có nhiều trong đó (chưa). – Freek

+0

Có, đã đồng ý, nhưng tôi đang nói về phạm vi đối tượng trong bối cảnh tạo các gói riêng biệt. Giả sử bạn có một số loại đối tượng Số lượng (Fowler) giúp tính toán và trình bày tổng số thứ tự. Và nó cũng hữu ích trong việc tính toán tổng số hàng tồn kho cho Kho lưu trữ. Ok rằng một trong những có thể được rõ ràng. Nhưng nếu cùng một đối tượng khách hàng là cần thiết trong hai gốc tổng hợp? Chúc mừng – Berryl

+0

Vâng Số lượng không thực sự 'liên kết' với gốc tôi đoán. Ngay cả khi bạn trả lại bằng cách nào đó, nó vẫn là một giá trị vì vậy nó sẽ không thay đổi (nếu không trả về một bản sao). Tôi nghĩ rằng nếu một lớp học là cần thiết trong tập hợp nhiều hơn nó có lẽ không thuộc về bất kỳ một trong những tập hợp (mặc dù tôi không có bằng chứng cho máy ATM này). Ngoài ra, tôi không biết một đối tượng có nên là gốc của hơn 1 tổng hợp không? Bạn có thể cho thấy một ví dụ? Thậm chí nó là, bạn có thể đặt đối tượng gốc trong một hội đồng riêng biệt, và cả hai con tổng hợp trong hội đồng khác nhau, sau đó sử dụng chỉ thị lắp ráp InternalsVisibleTo. – Freek

2

Ngoài (thú vị) khía cạnh lý thuyết của câu hỏi của bạn, tôi nghĩ rằng một câu trả lời thiết thực cho câu hỏi của bạn là sử dụng không gian tên để uẩn riêng biệt. Trong .NET, bạn đặt các lớp của bạn trong các không gian tên. Cấu trúc không gian tên độc lập với cấu trúc dự án và bạn có thể đặt nhiều không gian tên trong một dự án duy nhất mà trên các kết quả biên dịch trong một assembly đơn.

Bạn có thể sử dụng quyền này để đặt tất cả các lớp miền của mình trong một hội đồng duy nhất và vẫn có sự tách biệt giữa chúng.

+0

Có. Nhưng bạn vẫn không thể bảo vệ các thành viên khỏi các lớp trong không gian tên đầu tiên khỏi bị gọi từ các lớp trong không gian tên thứ hai, đó là mục tiêu của chúng ta. Từ những gì tôi có thể thấy, điều này có thể sử dụng các gói Java. Những gì bạn thực sự muốn làm là xoay nó xung quanh, sử dụng nhiều hội đồng với các đối tượng trong cùng một không gian tên. Bằng cách này bạn có tất cả các lớp miền của bạn trong cùng một NS, nhưng các thành viên của bạn vẫn được bảo vệ. – Freek

+0

Bạn cũng có thể làm điều đó. – Marijn

+0

Mục tiêu của bạn rõ ràng hơn với tôi bây giờ. Mặc dù vậy, tôi không thấy nó thực tế lắm, bởi vì bạn có thể muốn bảo vệ quyền truy cập vào các lớp này từ các tổng hợp khác từ quan điểm "tổng hợp ddd thuần túy" - nhưng bạn không muốn truy cập chúng bằng (ví dụ) các mô hình khung nhìn của bạn từ một hội đồng riêng biệt? – Marijn

0

Tôi không nghĩ rằng bạn muốn đặt định nghĩa lớp của "đối tượng phụ" bị ẩn cho các hội đồng khác. Bạn có thể đúng rằng bạn không thể tham chiếu chúng từ các gốc tổng hợp khác, nhưng nếu bạn muốn tạo mô hình trình bày dựa trên các đối tượng con này thì sao? Bạn sẽ cần truy cập vào chúng từ các hội đồng khác.

Vì vậy, từ quan điểm DDD, bạn có thể đúng, nhưng tôi không nghĩ bạn muốn mã của mình phản ánh điều này đến mức cực đoan mà bạn đề xuất. Điều này sẽ trở thành mã không thực tế cao.

+0

Thực ra bạn vẫn có thể cung cấp quyền truy cập vào một số thuộc tính của các đối tượng phụ. Để đọc, dù sao thì cũng chẳng sao. Để sửa đổi, bạn có quyền kiểm soát, và vì vậy gốc biết các đối tượng nào có thể được sửa đổi và có thể hành động tương ứng (ví dụ: bạn có thể cho phép khách hàng thay đổi thuộc tính 'mô tả' của một OrderLine nếu không có bất biến liên quan đến thuộc tính). Đối với các lớp trình bày, tôi nghĩ rằng nó sẽ rất không khôn ngoan để trực tiếp làm việc với các đối tượng miền trong lớp này (khớp nối lỏng lẻo, truy cập từ xa, tải chậm). – Freek

+0

Điều đó tùy thuộc vào loại phân tầng và cơ sở hạ tầng bạn chọn để sử dụng trong ứng dụng của mình; Tôi không nghĩ rằng nó là điều cần thiết để sử dụng các đối tượng miền trong một mô hình trình bày. – Marijn

+0

Tuy nhiên, tôi nhận được quan điểm của bạn về việc không cho phép truy cập vào các phương thức của các đối tượng con (cụ thể là các đối tượng thay đổi trạng thái) và bây giờ hiểu rõ hơn tại sao bạn muốn ngăn chặn truy cập vào các đối tượng đó. – Marijn

6

Tổng hợp là một trong những khái niệm khó nhất trong DDD. Bạn có hầu hết nó đúng. Tôi đề nghị thể hiện khái niệm về "thành viên" trong tổng hợp là đơn giản hơn việc giới thiệu thuật ngữ "subobjects".

Có một đối tượng không thể là thành viên của nhiều tập hợp. Người nào sẽ là người thi hành cuối cùng? Một tổng hợp gốc có thể dễ dàng làm mất hiệu lực một người khác bằng cách xóa các thành viên và mồ côi các thành viên khác trong tổng hợp khác. Bạn là chính xác, trong các tình huống mà một đối tượng sẽ xuất hiện cần phải là thành viên trong nhiều tập hợp, đối tượng đó phải là một thực thể độc lập, tức là đối tượng đó trở thành gốc của tổng hợp mới. (Có thể hoặc không thể có thêm thành viên, nhưng nếu nó không có thì tất nhiên nó sẽ trở thành tổng hợp của riêng nó.)

Và có, hoàn toàn đúng là tổng hợp tồn tại để thực thi bất biến. Nó cũng là một đơn vị công việc hoặc một giao dịch duy nhất về sự kiên trì. Một gốc tổng hợp chịu trách nhiệm cuối cùng cho tất cả các bất biến trên toàn bộ thành viên của nó, đó là bởi vì, ví dụ, thất bại của một biến thể có thể gây ra sự thất bại, và tổng hợp chịu trách nhiệm duy trì tập hợp như một đơn vị duy nhất có thể tồn tại .

Tuy nhiên, và đây là một phần tinh tế và khó khăn, đó là trách nhiệm cuối cùng không có nghĩa là tổng hợp cũng là chính chấp hành viên là tốt. Giống như những gì chúng tôi có trong hệ thống tư pháp - tòa án cuối cùng là địa điểm cuối cùng nơi các vấn đề của pháp luật được xác định và quy tắc cuối cùng của luật được áp đặt, bất biến được thực thi. Nhưng thực thi thực tế (và tuân thủ) xảy ra ở nhiều cấp độ của hệ thống. Trong thực tế, trong một xã hội được sắp xếp tốt, hầu hết các hoạt động áp đặt luật lệ - thực thi các bất biến - phải diễn ra tốt trước khi bạn đến tòa án, và bạn không phải dựa vào việc thường xuyên ra tòa ngay cả. (Mặc dù trong DDD, bạn luôn có thể muốn có một gốc tổng hợp thực hiện việc quét bất biến cuối cùng trước khi ví dụ như sự kiên trì.)

Những gì bạn đang đề xuất là khác nhau, về cơ bản toàn bộ xã hội của bạn ngoại trừ tòa án bị giam giữ, và bạn dường như thậm chí còn đề xuất rằng những người khác thậm chí không thể truy cập, họ chỉ có thể chuyển một thông điệp tới tòa án và hy vọng rằng nó hoạt động một cách thích hợp.

Hãy xem điều gì xảy ra với miền của bạn nếu bạn đi theo đường dẫn bạn đã đề xuất. Mục tiêu là tạo ra một mô hình miền phong phú và mang tính biểu cảm.Về ngôn ngữ phổ biến có ý nghĩa, bạn đã giảm vốn từ vựng làm việc của mình chỉ với các gốc tổng hợp. Một thực thể được cho là được truy cập bởi root tổng hợp vì bất biến nhưng cũng bởi vì nếu thiết kế là đúng, thực thể có bản sắc có ý nghĩa phát sinh từ thành viên của nó trong bối cảnh gốc tổng hợp của nó. Nhưng đề xuất của bạn là một thực thể thậm chí không có bất kỳ danh tính loại nào ngoài gốc tổng hợp của nó. Evans đặc biệt nói rằng đó là một phần của mục đích của một gốc tổng hợp - để cho phép các đối tượng để có được tài liệu tham khảo cho các thành viên bằng traversal. Nhưng bạn không thể có được một tham chiếu hữu ích vì một đối tượng khác thậm chí không nhận thức được rằng các kiểu thành viên của bạn tồn tại. Hoặc bạn có thể thay đổi không gian tên nhưng điều đó không tốt hơn nếu bạn không cho phép truyền tải. Bây giờ toàn bộ miền của bạn biết về các loại, nhưng loại đối tượng mà không bao giờ có thể thu được.

Thậm chí tệ hơn là điều xảy ra với thư mục gốc tổng hợp của bạn. Một tổng hợp gốc thường nên có lý do riêng của nó cho sự tồn tại, ngoài việc duy trì tính toàn vẹn tổng hợp. Nhưng bây giờ danh tính đó không còn rõ ràng nữa. Nó bị che khuất bởi sự cần thiết phải có một phương thức bao bọc cho tất cả các phần tử khác nhau và các thuộc tính của chúng. Những gì bạn nhận được là những nguồn gốc tổng hợp không còn có tính biểu cảm hay thậm chí là một bản sắc rõ ràng, chỉ là những vật thể thần thánh khó sử dụng.

Ví dụ về Đơn đặt hàng và Đơn đặt hàng của bạn là một trường hợp thú vị tại điểm. Lệnh này không cung cấp việc thực thi một số bất biến mà OrderLine yêu cầu thay mặt cho OrderLine. Nó đang kiểm soát hành động trong trường hợp này để thực thi bất biến riêng của nó. Đó là một hành động hợp lệ để đặt kiểm soát ion của gốc tổng hợp. Tuy nhiên, thường tập hợp chủ yếu là có liên quan với việc tạo ra đối tượng và/hoặc phá hủy.

Chắc chắn không có yêu cầu mô hình imposea nơi tất cả các thay đổi trong tiểu bang phải tự động được áp dụng bởi gốc tổng hợp và không bao giờ trực tiếp. Trong thực tế, điều này thường là lý do tại sao gốc tổng hợp cho phép truyền tải để lấy tham chiếu tới các thành viên - vì vậy một đối tượng bên ngoài có thể áp dụng thay đổi trạng thái, nhưng trong bối cảnh tổng hợp điều khiển vòng đời của thực thể thành viên bị thay đổi.

Không chỉ hiển thị, mà còn tương tác thực tế với miền lớn hơn thường là cơ bản để phát triển mô hình phong phú và biểu cảm. Các tập hợp phục vụ để kiểm soát truy cập đó nhưng không phải để loại bỏ nó hoàn toàn.

Tôi hy vọng điều này sẽ giúp, đó là một khái niệm khó để thảo luận.

+1

Tnx cho câu trả lời của bạn. * Nhưng đề xuất của bạn là một thực thể thậm chí không có bất kỳ nhận dạng loại nào ngoài gốc tổng hợp của nó. * Điều này là sai; các loại có thể truy cập được và các đối tượng được truyền xung quanh gốc. Điều bạn muốn đạt được là các thuộc tính nhất định chỉ có thể được thay đổi thông qua gốc. Điều này có nghĩa là bạn cần phải tìm một số sắp xếp cho phép chỉ root để truy cập các thuộc tính này của đối tượng 'member'. Trong Java, điều này có thể đạt được bằng cách đặt từng tập hợp trong một gói riêng biệt. Trong .NET, điều này dường như chỉ có thể bằng cách bao thanh toán mỗi tập hợp trong một assembly riêng biệt. – Freek

+0

@Sisyphus. Tốt đẹp, đặc biệt là trong bối cảnh của câu hỏi ở đây. Tổng hợp Root dễ dàng nhận được phiếu bầu của tôi như là khái niệm DDD * khó * nhất để bẻ khóa. Chúc mừng – Berryl

+0

OK. Toàn bộ nội dung phụ thực sự đã ném tôi. Tôi nghĩ rằng bạn không muốn các đối tượng bên ngoài có thể nhận được một tham chiếu đến một đối tượng và sau đó sửa đổi tất cả bên ngoài của gốc tổng hợp.Có nếu đặt tổng hợp của bạn trong hội đồng của riêng mình, bạn có thể phơi bày một số thuộc tính thành viên và chức năng, và phạm vi những người khác như nội bộ để ngăn chặn một đối tượng bên ngoài để root truy cập chúng. Tôi cho rằng tôi đặt điều này trong danh mục của p496 "Đừng viết khuôn khổ cho núm vú giả". Nếu nhóm dev không bị xử lý kỷ luật và cam kết với DDD thì cuối cùng họ sẽ chỉ thêm người truy cập công khai ... – Sisyphus