Các giá trị được tính toán và các bài đọc đơn giản - Một nỗi đau dai dẳng cho các thiết kế hướng miền của tôi!


9

Vấn đề tôi liên tục gặp phải là làm thế nào để xử lý các giá trị được tính toán theo logic miền trong khi vẫn hoạt động hiệu quả đối với việc lưu trữ dữ liệu.

Thí dụ:

Tôi đang trả lại danh sách Sản phẩm từ kho lưu trữ của mình thông qua dịch vụ. Danh sách này được giới hạn bởi thông tin phân trang từ yêu cầu DTO được gửi bởi khách hàng. Ngoài ra, DTO chỉ định một tham số sắp xếp (enum thân thiện với khách hàng).

Trong một kịch bản đơn giản, mọi thứ đều hoạt động tốt: dịch vụ gửi các biểu thức phân trang và sắp xếp tới repo và repo đưa ra một truy vấn hiệu quả cho DB.

Tuy nhiên, tất cả đã bị hỏng khi tôi cần sắp xếp các giá trị được tạo trong bộ nhớ từ mô hình miền của mình. Ví dụ, lớp Product có phương thức IsExpired () trả về một bool dựa trên logic nghiệp vụ. Bây giờ tôi không thể sắp xếp và trang ở cấp repo - tất cả sẽ được thực hiện trong bộ nhớ (không hiệu quả) và dịch vụ của tôi sẽ phải biết những điều phức tạp khi nào nên đưa các thông số này cho repo và khi nào thực hiện phân loại / phân trang chinh no.

Mẫu duy nhất có vẻ hợp lý với tôi là lưu trữ trạng thái của thực thể trong db (biến IsExpired () thành trường chỉ đọc và cập nhật thông qua logic miền trước khi lưu). Nếu tôi tách logic này thành một kho lưu trữ "mô hình đọc / dto" và "báo cáo" riêng biệt, thì tôi đang làm cho mô hình của mình bị thiếu máu nhiều hơn tôi muốn.

BTW, mọi ví dụ tôi đã thấy ngoài kia cho các tính toán như thế này dường như thực sự dựa vào xử lý trong bộ nhớ và che đậy thực tế rằng nó kém hiệu quả hơn về lâu dài. Có lẽ tôi đang tối ưu hóa sớm, nhưng điều đó không phù hợp với tôi.

Tôi rất muốn nghe cách người khác giải quyết vấn đề này vì tôi chắc chắn rằng nó phổ biến trong gần như dự án liên quan đến DDD.

Câu trả lời:


3

Tôi không nghĩ rằng việc có hai mô hình miền khác nhau của cùng một mô hình dữ liệu sẽ khiến miền của bạn bị thiếu máu. Một mô hình miền thiếu máu là một mô hình mà logic kinh doanh thay đổi thường bị ẩn khỏi miền trong một lớp dịch vụ (hoặc tệ hơn là trong lớp UI).

Việc tách các mô hình miền truy vấn và lệnh được sử dụng thường xuyên và có một từ viết tắt hay mà bạn có thể google trong CQRS (Phân chia trách nhiệm truy vấn lệnh).

Sử dụng mô hình mô hình miền, Udi Dahan

Mặc dù trước đây tôi đã "thành công" khi tạo ra một mô hình đối tượng liên tục duy nhất xử lý cả lệnh và truy vấn, nhưng thường rất khó để mở rộng nó, vì mỗi phần của hệ thống kéo mô hình theo một hướng khác nhau.

Nó chỉ ra rằng các nhà phát triển thường đảm nhận các yêu cầu vất vả hơn so với nhu cầu thực sự của doanh nghiệp. Quyết định sử dụng các thực thể mô hình miền để hiển thị thông tin cho người dùng chỉ là một ví dụ như vậy.

[...]

Đối với những người đủ tuổi để ghi nhớ, các thực tiễn tốt nhất về việc sử dụng COM + đã hướng dẫn chúng tôi tạo các thành phần riêng biệt cho chỉ đọc và logic đọc-ghi. Ở đây chúng ta, một thập kỷ sau, với các công nghệ mới như Entity Framework, nhưng những nguyên tắc tương tự vẫn tiếp tục được giữ vững.

CQRS với các diễn viên Akka và các mô hình miền chức năng, Debasish Ghosh

Greg Young đã cung cấp một số phiên tuyệt vời về DDD và CQRS. Năm 2008, ông nói "Một mô hình duy nhất không thể phù hợp để báo cáo, tìm kiếm và hành vi giao dịch". Chúng tôi có ít nhất hai mô hình - một mô hình xử lý các lệnh và cung cấp các thay đổi cho một mô hình khác phục vụ các truy vấn và báo cáo của người dùng. Hành vi giao dịch của ứng dụng được thực thi thông qua mô hình miền tổng hợp và kho lưu trữ, trong khi các truy vấn được phục vụ trực tiếp từ mô hình dữ liệu không chuẩn hóa.

CQRS, Martin Fowler

Thay đổi mà CQRS giới thiệu là chia mô hình khái niệm đó thành các mô hình riêng biệt để cập nhật và hiển thị, được gọi là Lệnh và Truy vấn tương ứng theo từ vựng của CommandQuerySeparation. Lý do là đối với nhiều vấn đề, đặc biệt là trong các lĩnh vực phức tạp hơn, việc có cùng một mô hình khái niệm cho các lệnh và truy vấn dẫn đến một mô hình phức tạp hơn mà không tốt.

Nói tóm lại, ý tưởng của bạn về việc mô hình lệnh xử lý hết hạn và chuyển nó vào cơ sở dữ liệu là hoàn toàn tốt. Đọc qua bài viết đầu tiên ở trên và bạn sẽ thấy các kịch bản tương tự nhưng phức tạp hơn.


2

SỰ CHỈ RÕ

Tôi biết bạn đã chấp nhận câu trả lời, nhưng, bạn đã hỏi về DDD và kết quả chính xác cho điều này là cái mà Evans gọi là 'đặc tả':
liên kết sách google trực tiếp
Nếu liên kết đó không hoạt động, hãy kiểm tra sách trong các kết quả này
Đó là trang 226 nếu bạn có cuốn sách.

Trên trang 227 có 3 cách sử dụng cho các thông số kỹ thuật: Xác thực, Chọn lọc, Xây dựng một đối tượng đặc biệt mới. Của bạn là 'lựa chọn' - IsExpired.

Một điều khác về khái niệm 'đặc thù' là nó thừa nhận rằng - vì mục đích hiệu quả - bạn có thể cần một phiên bản mã để hoạt động trên các đối tượng trong bộ nhớ và một phiên bản mã khác để truy vấn kho lưu trữ một cách hiệu quả mà không cần phải lấy trước Tất cả các đối tượng vào bộ nhớ.

Trong một thế giới đơn giản, điều này có nghĩa là đưa một phiên bản SQL vào kho lưu trữ của bạn và một phiên bản đối tượng trong mô hình của bạn, tất nhiên có nhược điểm. Logic ở 2 nơi (xấu, ai đó sẽ quên cập nhật về những nơi đó) và có logic miền trong kho lưu trữ của bạn.

Vì vậy, câu trả lời là đặt cả hai bộ logic trong một đặc tả. Phiên bản trong bộ nhớ, rõ ràng, nhưng một phiên bản kho lưu trữ, quá. Nếu bạn đang sử dụng ví dụ n-hibernate, bạn có thể sử dụng ngôn ngữ truy vấn tích hợp sẵn cho phiên bản kho lưu trữ.

Nếu không, bạn sẽ phải tạo một phương thức lưu trữ đặc biệt cho đặc tả này được sử dụng từ đối tượng đặc tả. Các lệnh gọi collecitons của các đối tượng phù hợp với đặc tả sẽ chuyển qua đặc tả, không phải kho lưu trữ. Và ít nhất mã này hét lên 'tôi đang ở 2 nơi, đừng quên điều đó' với những người duy trì trong tương lai. Có một ví dụ tuyệt vời trên trang 231-232 cho một vấn đề rất giống nhau.

Đặc điểm kỹ thuật là "rò rỉ / trượt" cho phép về độ tinh khiết của DDD. Nó vẫn có thể không phục vụ nhu cầu của bạn cho nhiều mục đích. Ví dụ: ORM có thể tạo SQL xấu; có thể có quá nhiều mã hóa thêm. Vì vậy, bạn có thể phải gọi các phương thức kho lưu trữ gần giống như đưa SQL vào đặc tả. Một điều xấu tất nhiên. Nhưng đừng quên, chương trình của bạn phải hoạt động - với tốc độ hợp lý. Nó không phải giành được một giải thưởng tinh khiết DDD. Vì vậy, một thực tế của việc chuyển đổi kho dữ liệu có thể có nghĩa là phẫu thuật cũ lỗi thời trong chương trình. Cũng là một điều xấu. Nhưng không tệ như một chương trình chậm (hay còn gọi là SỐC). Nếu việc chạy ra khỏi hộp trên các DB khác nhau là một thực tế, rõ ràng, bạn sẽ sao chép các quy tắc kinh doanh cho từng kho dữ liệu cho từng đặc tả. Ít nhất bạn có ngón tay của bạn về vấn đề này và bạn có thể sử dụng mô hình chiến lược khi bạn trao đổi các kho lưu trữ. Nhưng nếu bạn đang sử dụng một DB cụ thể đã nhớYAGNI.

Về CQRS: Trích dẫn của Fowler bởi pdr ở trên vẫn đúng ở đây: "có cùng một mô hình khái niệm cho các lệnh và truy vấn dẫn đến một mô hình phức tạp hơn mà không tốt" ... và bạn có thể cần sử dụng CQRS hoặc tương tự. Nhưng nó đắt hơn nhiều từ quan điểm phát triển và bảo trì. Nếu bạn là một nhà cung cấp gói cạnh tranh với những người khác, nó có thể trả tiền. Nếu bạn đang viết một ứng dụng LOB tùy chỉnh cho một khách hàng, chụp cho sự hoàn hảo là một lựa chọn kém. Bạn cần phải quyết định xem giá trị trong việc có một mô hình hoàn toàn hoặc chủ yếu là gấp đôi có xứng đáng với nỗ lực bổ sung hay không. Sự chỉ rõlà một sự thỏa hiệp tốt vì nó cho phép bạn thực hiện sự tách biệt này chỉ trong một phần nhỏ của chương trình cần nó, với tốc độ (phát triển) và sự đơn giản của một mô hình. Chúc may mắn!


Điều đó làm cho ý nghĩa hoàn hảo. Tôi nghĩ rằng tôi cần phải cắn viên đạn và đọc cuốn sách của Evans :-) Bây giờ tôi đang thấy rằng sự hiểu biết nông cạn về những khái niệm này thực sự có thể làm bạn tê liệt!
drogon

0

Tôi đoán tôi sẽ đặt câu hỏi logic kinh doanh là gì để xác định liệu isExpired có đúng hay không. Logic này có thể được thực hiện bởi một truy vấn khi mô hình dữ liệu đứng không? Nếu vậy, bạn có thể làm cho kho lưu trữ của mình đủ thông minh để sử dụng logic "isExpired" khi bạn yêu cầu nó cho Sản phẩm theo một cách nhất định không? Nếu không, có lẽ bạn cần phải xem lại mô hình dữ liệu của mình.

DDD không có nghĩa là kho lưu trữ của bạn cần phải ngu ngốc - nó chỉ có nghĩa là tên miền của bạn cần biết cách nói chuyện với kho lưu trữ của bạn.

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.