Là sự cô lập mô hình miền / kiên trì thường khó xử này?


12

Tôi đang đi sâu vào các khái niệm về Thiết kế hướng tên miền (DDD) và thấy một số nguyên tắc kỳ lạ, đặc biệt là liên quan đến sự cô lập của mô hình miền và mô hình bền bỉ. Đây là sự hiểu biết cơ bản của tôi:

  1. Một dịch vụ trên lớp ứng dụng (cung cấp một bộ tính năng) yêu cầu các đối tượng miền từ kho lưu trữ mà nó cần để thực hiện chức năng của nó.
  2. Việc triển khai cụ thể của kho lưu trữ này lấy dữ liệu từ bộ lưu trữ được triển khai cho
  3. Dịch vụ báo cho đối tượng miền, đóng gói logic nghiệp vụ, để thực hiện một số tác vụ nhất định nhằm sửa đổi trạng thái của nó.
  4. Dịch vụ báo cho kho lưu trữ để duy trì đối tượng miền đã sửa đổi.
  5. Kho lưu trữ cần ánh xạ đối tượng miền trở lại biểu diễn tương ứng trong bộ lưu trữ.

Minh họa dòng chảy

Bây giờ, với các giả định ở trên, những điều sau đây có vẻ khó xử:

Quảng cáo 2.:

Mô hình miền dường như tải toàn bộ đối tượng miền (bao gồm tất cả các trường và tham chiếu), ngay cả khi chúng không cần thiết cho chức năng yêu cầu nó. Tải hoàn toàn thậm chí có thể không khả thi nếu các đối tượng miền khác được tham chiếu, trừ khi bạn cũng tải các đối tượng miền đó và tất cả các đối tượng mà chúng tham chiếu lần lượt, v.v. Tải lười biếng xuất hiện trong tâm trí, điều đó có nghĩa là bạn bắt đầu truy vấn các đối tượng miền của mình, đây là trách nhiệm của kho lưu trữ ở nơi đầu tiên.

Do vấn đề này, cách tải "đối tượng" chính xác dường như có chức năng tải chuyên dụng cho từng trường hợp sử dụng. Các chức năng chuyên dụng này sau đó sẽ chỉ tải dữ liệu theo yêu cầu của trường hợp sử dụng mà chúng được thiết kế cho. Đây là nơi mà sự lúng túng xuất hiện: Đầu tiên, tôi sẽ phải duy trì một lượng đáng kể các hàm tải cho mỗi lần triển khai kho lưu trữ và các đối tượng miền sẽ kết thúc ở trạng thái không hoàn chỉnh mang nulltrong các trường của chúng. Cái sau về mặt kỹ thuật không phải là một vấn đề bởi vì nếu một giá trị không được tải, thì nó không được yêu cầu bởi chức năng đã yêu cầu nó. Vẫn còn lúng túng và là một mối nguy hiểm tiềm tàng.

Quảng cáo 3.:

Làm thế nào một đối tượng miền sẽ xác minh các ràng buộc duy nhất khi xây dựng nếu nó không có bất kỳ khái niệm nào về kho lưu trữ? Chẳng hạn, nếu tôi muốn tạo một cái mới Uservới số an sinh xã hội duy nhất (được đưa ra), xung đột sớm nhất sẽ xảy ra khi yêu cầu kho lưu trữ đối tượng, chỉ khi có một ràng buộc duy nhất được xác định trên cơ sở dữ liệu. Mặt khác, tôi có thể tìm kiếm một Uservới an sinh xã hội nhất định và báo cáo lỗi trong trường hợp nó tồn tại, trước khi tạo một cái mới. Nhưng sau đó, kiểm tra ràng buộc sẽ sống trong dịch vụ chứ không phải trong đối tượng miền nơi chúng thuộc về. Tôi chỉ nhận ra rằng các đối tượng miền được phép sử dụng (kho chứa) rất tốt để xác nhận.

Quảng cáo 5.:

Tôi nhận thấy việc ánh xạ các đối tượng miền vào một phụ trợ lưu trữ như là một quá trình đòi hỏi nhiều công việc so với việc các đối tượng miền sửa đổi trực tiếp dữ liệu lót. Tất nhiên, đó là một điều kiện tiên quyết thiết yếu để tách rời việc thực hiện lưu trữ cụ thể khỏi mã miền. Tuy nhiên, nó thực sự đến với chi phí cao như vậy?

Bạn rõ ràng có tùy chọn sử dụng các công cụ ORM để thực hiện ánh xạ cho bạn. Tuy nhiên, những điều này thường yêu cầu bạn thiết kế mô hình miền theo các hạn chế của ORM, hoặc thậm chí giới thiệu một sự phụ thuộc từ miền đến lớp cơ sở hạ tầng (ví dụ bằng cách sử dụng các chú thích ORM trong các đối tượng miền). Ngoài ra, tôi đã đọc rằng các ORM giới thiệu một chi phí tính toán đáng kể.

Trong trường hợp cơ sở dữ liệu NoQuery, hầu như không tồn tại bất kỳ khái niệm giống ORM nào, làm thế nào để bạn theo dõi các thuộc tính nào đã thay đổi trong các mô hình miền trên save()?

Chỉnh sửa : Ngoài ra, để kho lưu trữ truy cập trạng thái của đối tượng miền (tức là giá trị của từng trường), đối tượng miền cần tiết lộ trạng thái bên trong của nó phá vỡ đóng gói.

Nói chung:

  • Logic giao dịch sẽ đi về đâu? Điều này chắc chắn là kiên trì cụ thể. Một số cơ sở hạ tầng lưu trữ thậm chí có thể không hỗ trợ các giao dịch (như kho lưu trữ giả trong bộ nhớ).
  • Đối với các hoạt động hàng loạt sửa đổi nhiều đối tượng, tôi có phải tải, sửa đổi và lưu trữ từng đối tượng riêng lẻ để đi qua logic xác thực được đóng gói của đối tượng không? Điều đó trái ngược với việc thực hiện một truy vấn trực tiếp trên cơ sở dữ liệu.

Tôi sẽ đánh giá cao một số làm rõ về chủ đề này. Những giả định của tôi có đúng không? Nếu không, cách chính xác để giải quyết những vấn đề này là gì?


1
Điểm tốt và câu hỏi, tôi cũng quan tâm đến những người. Lưu ý một mặt - nếu bạn mô hình hóa tổng hợp một cách chính xác có nghĩa là tại bất kỳ thời điểm tồn tại nào, thì thể hiện tổng hợp phải ở trạng thái hợp lệ - đó là điểm chính của tổng hợp (và không sử dụng tổng hợp làm vùng chứa thành phần). Điều đó cũng có nghĩa là để khôi phục dữ liệu DB tổng hợp, kho lưu trữ thường sẽ phải sử dụng hàm tạo cụ thể và tập hợp các hoạt động đột biến và tôi không thấy bất kỳ ORM nào có thể tự động biết cách thực hiện các hoạt động đó .
Dusan

2
Điều đáng thất vọng hơn nữa là những câu hỏi như của bạn thường được hỏi xung quanh, nhưng, theo hiểu biết của tôi - có những ví dụ KHÔNG PHẢI về việc thực hiện các tập hợp và kho lưu trữ trong cuốn sách
Dusan

Câu trả lời:


5

Hiểu biết cơ bản của bạn là chính xác và kiến ​​trúc bạn phác thảo là tốt và hoạt động tốt.

Đọc giữa các dòng có vẻ như bạn đang đến từ một phong cách lập trình hoạt động tập trung vào cơ sở dữ liệu hơn? Để có được một triển khai làm việc tôi sẽ nói bạn cần phải

1: Các đối tượng miền không phải bao gồm toàn bộ biểu đồ đối tượng. Ví dụ tôi có thể có:

public class Customer
{
    public string AddressId {get;set;}
    public string Name {get;set;}
}

public class Address
{
    public string Id {get;set;}
    public string HouseNumber {get;set;
}

Địa chỉ và Khách hàng chỉ cần là một phần của cùng một tổng hợp nếu bạn có một số logic như "tên khách hàng chỉ có thể bắt đầu bằng cùng một chữ cái với tên hous". Bạn có quyền tránh lười biếng tải và các phiên bản 'Lite' của các đối tượng.

2: Các ràng buộc về tính duy nhất thường là mục đích của kho lưu trữ không phải là đối tượng miền. Đừng tiêm các kho lưu trữ vào các Đối tượng Miền, đó là việc quay trở lại bản ghi hoạt động, chỉ đơn giản là lỗi khi dịch vụ cố lưu.

Quy tắc kinh doanh không phải là "Không có hai trường hợp Người dùng có cùng SocialSecurityNumber có thể tồn tại cùng một lúc"

Đó là họ không thể tồn tại trên cùng một kho lưu trữ.

3: Không khó để viết các kho lưu trữ hơn là các phương pháp cập nhật thuộc tính riêng lẻ. Thực tế, bạn sẽ thấy rằng bạn có khá nhiều mã giống nhau. Nó chỉ là lớp bạn đặt nó vào.

ORM ngày nay rất dễ dàng và không có ràng buộc thêm về mã của bạn. Phải nói rằng, cá nhân tôi thích chỉ đơn giản là tay quay SQL. Điều đó không khó lắm, bạn không bao giờ gặp phải bất kỳ vấn đề nào với các tính năng ORM và bạn có thể tối ưu hóa khi cần thiết.

Thực sự không cần phải theo dõi các thuộc tính nào đã thay đổi khi bạn lưu. Giữ các đối tượng miền của bạn nhỏ và chỉ cần ghi đè lên phiên bản cũ.

Câu hỏi chung

  1. Logic giao dịch đi trong kho lưu trữ. Nhưng bạn không nên có nhiều nếu có. Chắc chắn bạn cần một số nếu bạn có các bảng con trong đó bạn đang đặt các đối tượng con của tổng hợp, nhưng điều đó sẽ được gói gọn trong phương thức kho lưu trữ SaveMyObject.

  2. Cập nhật hàng loạt. Có, bạn nên thay đổi riêng từng đối tượng sau đó chỉ cần thêm phương thức SaveMyObjects (List object) vào kho lưu trữ của bạn, để thực hiện cập nhật hàng loạt.

    Bạn muốn Đối tượng miền hoặc Dịch vụ miền chứa logic. Không phải cơ sở dữ liệu. Điều đó có nghĩa là bạn không thể thực hiện "cập nhật tên khách hàng đặt = x trong đó y", bởi vì đối với tất cả những gì bạn biết đối tượng Khách hàng hoặc CustomerUpdateService thực hiện 20 điều kỳ lạ khác khi bạn thay đổi tên.


Câu trả lời chính xác. Bạn hoàn toàn đúng, tôi đã quen với một phong cách mã hóa hoạt động, đó là lý do tại sao mẫu kho lưu trữ có vẻ kỳ lạ ngay từ cái nhìn đầu tiên. Tuy nhiên, không phải các đối tượng miền "nạc" ( AddressIdthay vì Address) mâu thuẫn với các nguyên tắc OO?
Đôi M

Không, bạn vẫn có một đối tượng Địa chỉ, nó không phải là con của Khách hàng
Ewan

lập bản đồ đối tượng quảng cáo mà không thay đổi phần mềm
Double M

2

Câu trả lời ngắn: Sự hiểu biết của bạn là chính xác và các câu hỏi bạn có chỉ ra các vấn đề hợp lệ mà các giải pháp không được chuyển thẳng cũng không được chấp nhận phổ biến.

Điểm 2.: (Tải biểu đồ đối tượng đầy đủ)

Tôi không phải là người đầu tiên chỉ ra rằng ORM không phải lúc nào cũng là một giải pháp tốt. Vấn đề chính là các ORM không biết gì về trường hợp sử dụng thực tế, vì vậy họ không biết phải tải gì hoặc làm thế nào để tối ưu hóa. Đây là một vấn đề.

Như bạn đã nói, giải pháp rõ ràng là có các phương pháp kiên trì cho từng trường hợp sử dụng. Nhưng nếu bạn vẫn sử dụng ORM cho điều đó, ORM sẽ buộc bạn đóng gói mọi thứ vào các đối tượng dữ liệu. Mà ngoài việc không thực sự hướng đối tượng, một lần nữa không phải là thiết kế tốt nhất cho một số trường hợp sử dụng.

Nếu tôi chỉ muốn cập nhật hàng loạt một số hồ sơ thì sao? Tại sao tôi cần một đại diện đối tượng cho tất cả các hồ sơ? Vân vân.

Vì vậy, giải pháp cho vấn đề đó là không sử dụng ORM cho các trường hợp sử dụng mà nó không phù hợp. Thực hiện một trường hợp sử dụng "một cách tự nhiên", đôi khi không yêu cầu "sự trừu tượng hóa" bổ sung của chính dữ liệu (đối tượng dữ liệu) cũng như sự trừu tượng hóa trên "bảng" (kho lưu trữ).

Có một nửa các đối tượng dữ liệu hoặc thay thế các tham chiếu đối tượng bằng "id" là cách giải quyết tốt nhất, không phải là thiết kế tốt, như bạn đã chỉ ra.

Điểm 3.: (Kiểm tra các ràng buộc)

Nếu sự kiên trì không được trừu tượng hóa, mỗi trường hợp sử dụng rõ ràng có thể kiểm tra bất kỳ ràng buộc nào nó muốn một cách dễ dàng. Yêu cầu mà các đối tượng không biết "kho lưu trữ" là hoàn toàn nhân tạo và không phải là vấn đề của công nghệ.

Điểm 5.: (ORM)

Tất nhiên, đó là một điều kiện tiên quyết thiết yếu để tách rời việc thực hiện lưu trữ cụ thể khỏi mã miền. Tuy nhiên, nó thực sự đến với chi phí cao như vậy?

Không, nó không. Có rất nhiều cách khác để có sự kiên trì. Vấn đề là ORM luôn được xem là "giải pháp" để sử dụng (đối với cơ sở dữ liệu quan hệ ít nhất). Cố gắng đề nghị không sử dụng nó cho một số trường hợp sử dụng trong dự án là vô ích và tùy thuộc vào chính ORM đôi khi thậm chí là không thể, vì bộ nhớ cache và thực thi muộn đôi khi được sử dụng bởi các công cụ này.

Câu hỏi chung 1.: (Giao dịch)

Tôi không nghĩ có một giải pháp duy nhất. Nếu thiết kế của bạn hướng đối tượng, sẽ có một phương thức "hàng đầu" cho mỗi trường hợp sử dụng. Các giao dịch nên ở đó.

Bất kỳ hạn chế khác là hoàn toàn nhân tạo.

Câu hỏi chung 2.: (Hoạt động số lượng lớn)

Với một ORM, bạn (đối với hầu hết các ORM mà tôi biết) bị buộc phải đi qua các đối tượng riêng lẻ. Điều này là hoàn toàn không cần thiết và có lẽ sẽ không phải là thiết kế của bạn nếu bàn tay của bạn không bị ràng buộc bởi ORM.

Yêu cầu tách "logic" khỏi SQL xuất phát từ các ORM. Họ phải nói rằng, vì họ không thể hỗ trợ nó. Nó không phải là "xấu".

Tóm lược

Tôi đoán quan điểm của tôi là ORM không phải lúc nào cũng là công cụ tốt nhất cho một dự án và thậm chí nếu có, nó rất khó có thể là tốt nhất cho tất cả các trường hợp sử dụng trong một dự án.

Tương tự, sự trừu tượng hóa kho dữ liệu của DDD không phải lúc nào cũng tốt nhất. Tôi thậm chí sẽ đi xa để nói, chúng hiếm khi là thiết kế tối ưu.

Điều đó khiến chúng tôi không có giải pháp phù hợp với một kích thước, vì vậy chúng tôi sẽ phải suy nghĩ về các giải pháp cho từng trường hợp sử dụng riêng lẻ, đây không phải là tin tốt và rõ ràng làm cho công việc của chúng tôi khó khăn hơn :)


Những điểm rất thú vị ở đó, cảm ơn bạn đã xác nhận những giả định của tôi. Bạn nói có rất nhiều cách khác để có sự kiên trì. Bạn có thể đề xuất một mẫu thiết kế biểu diễn được sử dụng với cơ sở dữ liệu đồ thị (không có ORM), mà vẫn sẽ cung cấp PI?
Double M

1
Tôi thực sự sẽ đặt câu hỏi liệu bạn có cần cách ly (và loại nào) ngay từ đầu không. Cách ly trên mỗi công nghệ (ví dụ: cơ sở dữ liệu, ui, v.v.) mang lại gần như tự động "sự vụng về" mà bạn đang cố gắng tránh, vì lợi thế của việc thay thế công nghệ cơ sở dữ liệu dễ dàng hơn. Tuy nhiên, chi phí là một thay đổi khó khăn hơn trong logic kinh doanh vì nó trải qua các lớp. Hoặc bạn có thể phân chia theo các chức năng kinh doanh, điều này sẽ làm thay đổi cơ sở dữ liệu khó hơn, nhưng thay đổi logic dễ dàng hơn. Bạn thực sự muốn cái nào?
Robert Bräutigam

1
Bạn có thể có hiệu suất tốt nhất nếu bạn chỉ mô hình hóa miền (tức là các chức năng kinh doanh) và không trừu tượng hóa cơ sở dữ liệu (cho dù quan hệ hay biểu đồ không quan trọng). Vì cơ sở dữ liệu không được trừu tượng hóa từ ca sử dụng, nên ca sử dụng có thể thực hiện các truy vấn / cập nhật tối ưu nhất mà nó muốn và không cần phải thông qua một số mô hình đối tượng khó xử để thực hiện những gì nó muốn.
Robert Bräutigam

Vâng, mục tiêu chính là để giữ mối quan tâm của sự kiên trì ra khỏi logic kinh doanh, để có mã sạch dễ hiểu, mở rộng và kiểm tra. Có thể trao đổi công nghệ DB chỉ là một phần thưởng. Tôi có thể thấy rằng rõ ràng có ma sát giữa hiệu quả và sự thiếu hiểu biết, dường như mạnh hơn với các DB đồ thị do các truy vấn mạnh mẽ mà bạn có thể (nhưng không được phép) sử dụng.
Double M

1
Là một nhà phát triển Java Enterprise, tôi có thể nói với bạn rằng chúng tôi đã cố gắng tách sự kiên trì khỏi logic trong hai thập kỷ qua. Nó không hoạt động. Đầu tiên, sự tách biệt không bao giờ thực sự đạt được. Thậm chí ngày nay, có tất cả các loại công cụ liên quan đến cơ sở dữ liệu trong các đối tượng được cho là "kinh doanh", trong đó chính là id cơ sở dữ liệu (và rất nhiều chú thích cơ sở dữ liệu). Thứ hai, như bạn đã nói, đôi khi logic nghiệp vụ được thực thi trên cơ sở dữ liệu theo bất kỳ cách nào. Thứ ba, đó là lý do chúng tôi có cơ sở dữ liệu cụ thể, để có thể giảm tải một số logic được thực hiện tốt nhất ở nơi có dữ liệu.
Robert Bräutigam
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.