Phiên bản ngắn
Lý do của DDD là các Đối tượng Miền là các trừu tượng sẽ đáp ứng các yêu cầu miền chức năng của bạn - nếu Đối tượng Miền không thể dễ dàng thực hiện các yêu cầu đó, điều đó cho thấy bạn có thể đang sử dụng trừu tượng hóa sai.
Đặt tên các đối tượng miền bằng cách sử dụng Danh từ thực thể có thể dẫn đến các đối tượng đó trở nên gắn kết chặt chẽ với nhau và trở thành đối tượng "thần" cồng kềnh và họ có thể đưa ra các vấn đề như câu hỏi trong câu hỏi này như "Nơi nào là nơi thích hợp để đặt Phương thức tạo hay hơn? ".
Để giúp dễ dàng xác định Root tổng hợp 'đúng', hãy xem xét một cách tiếp cận khác trong đó Đối tượng miền dựa trên các yêu cầu nghiệp vụ cấp cao chức năng - tức là chọn các danh từ ám chỉ các yêu cầu chức năng và / hoặc hành vi mà người dùng hệ thống cần biểu diễn.
Phiên bản dài
DDD là một cách tiếp cận với OO Design nhằm tạo ra một biểu đồ các Đối tượng miền trong Lớp nghiệp vụ trong hệ thống của bạn - Các đối tượng miền chịu trách nhiệm đáp ứng các yêu cầu nghiệp vụ cấp cao của bạn và lý tưởng nhất là có thể dựa vào Lớp dữ liệu cho những thứ như hiệu suất và tính toàn vẹn của kho dữ liệu liên tục bên dưới.
Một cách khác để xem xét nó có thể là các gạch đầu dòng trong danh sách này
- Danh từ thực thể thường đề xuất các thuộc tính dữ liệu.
- Danh từ tên miền nên đề xuất hành vi
- Mô hình DDD và OO liên quan đến trừu tượng dựa trên các yêu cầu chức năng và logic kinh doanh / miền cốt lõi.
- Lớp Logic nghiệp vụ chịu trách nhiệm đáp ứng các yêu cầu miền cấp cao
Một trong những quan niệm sai lầm phổ biến liên quan đến DDD là Đối tượng miền phải dựa trên một số "vật" trong thế giới thực (nghĩa là một số danh từ mà bạn có thể chỉ ra trong thế giới thực, được gán cho tất cả các loại dữ liệu / thuộc tính), tuy nhiên dữ liệu / thuộc tính của những thứ trong thế giới thực không nhất thiết phải là điểm khởi đầu tốt khi cố gắng đưa ra các yêu cầu chức năng.
Tất nhiên, Business Logic nên sử dụng dữ liệu này, nhưng bản thân các Đối tượng Miền phải là trừu tượng đại diện cho các yêu cầu và hành vi của Miền chức năng.
Ví dụ; các danh từ như Order
hoặc Customer
không ngụ ý bất kỳ hành vi nào, và do đó nói chung là trừu tượng không có ích để đại diện cho logic kinh doanh và Đối tượng miền.
Khi tìm kiếm các loại trừu tượng có thể hữu ích để biểu diễn Logic nghiệp vụ, hãy xem xét các yêu cầu điển hình mà bạn có thể mong đợi một hệ thống đáp ứng:
- Là một nhân viên bán hàng, tôi muốn tạo Đơn hàng cho một khách hàng mới để tôi có thể tạo hóa đơn cho các Sản phẩm được bán với Giá và Số lượng của họ.
- Với tư cách là Cố vấn dịch vụ khách hàng, tôi muốn hủy Đơn đặt hàng đang chờ xử lý để Đơn hàng không được Nhà điều hành kho thực hiện.
- Với tư cách là Cố vấn dịch vụ khách hàng, tôi muốn trả lại một dòng đơn hàng để có thể điều chỉnh Sản phẩm thành hàng tồn kho và Thanh toán được hoàn lại thông qua phương thức Thanh toán ban đầu của Khách hàng.
- Là Nhà điều hành kho, tôi muốn xem tất cả các Sản phẩm trên Đơn hàng đang chờ xử lý và thông tin Giao hàng để tôi có thể Chọn sản phẩm và gửi chúng qua Chuyển phát nhanh.
- Vân vân.
Mô hình hóa các yêu cầu miền với phương pháp DDD
Dựa trên danh sách trên, hãy xem xét một số Đối tượng Miền tiềm năng cho hệ thống Đơn hàng như vậy:
SalesOrderCheckout
PendingOrdersStream
WarehouseOrderDespatcher
OrderRefundProcessor
Là các đối tượng miền, chúng đại diện cho các khái niệm trừu tượng có quyền sở hữu các yêu cầu miền hành vi khác nhau; thực sự danh từ của họ gợi ý mạnh mẽ về (các) yêu cầu chức năng cụ thể mà họ đáp ứng.
(Có thể có thêm cơ sở hạ tầng trong đó, chẳng hạn như EventMediator
để chuyển thông báo cho người quan sát muốn biết khi nào đơn hàng mới được tạo hoặc khi đơn hàng được giao, v.v.).
Ví dụ: SalesOrderCheckout
có thể cần xử lý dữ liệu về Khách hàng, Giao hàng và Sản phẩm, tuy nhiên không liên quan đến bất kỳ điều gì liên quan đến hành vi đối với các đơn đặt hàng vận chuyển, sắp xếp các đơn đặt hàng đang chờ xử lý hoặc hoàn lại tiền.
Để SalesOrderCheckout
thực hiện các yêu cầu miền của nó bao gồm thực thi các quy tắc kinh doanh đó như ngăn khách hàng đặt mua quá nhiều mặt hàng, có thể chạy một số xác thực và có thể tăng thông báo cho các bộ phận khác của hệ thống - nó có thể thực hiện tất cả những điều đó mà không nhất thiết phải phụ thuộc vào bất kỳ của các đối tượng khác.
DDD sử dụng Danh từ thực thể để thể hiện các Đối tượng Miền
Có một số nguy hiểm tiềm ẩn khi điều trị các danh từ đơn giản như Order
, Customer
và Product
như Domain Objects; trong số những vấn đề đó có những vấn đề bạn ám chỉ trong câu hỏi:
- Nếu một phương thức xử lý một
Order
, a Customer
và a Product
, thì đối tượng miền nào thuộc về nó?
- Root tổng hợp cho 3 đối tượng đó ở đâu?
Nếu bạn chọn Danh từ thực thể để đại diện cho Đối tượng miền, một số điều có thể xảy ra:
Order
, Customer
Và Product
có nguy cơ phát triển thành "thần" đối tượng
- Nguy cơ kết thúc với một
Manager
đối tượng thần duy nhất để gắn kết mọi thứ lại với nhau.
- Các đối tượng đó có nguy cơ trở nên gắn kết chặt chẽ với nhau - có thể khó thực hiện các yêu cầu miền mà không vượt qua
this
(hoặc self
)
- Nguy cơ phát triển trừu tượng "rò rỉ" - tức là các đối tượng miền được dự kiến sẽ phơi bày hàng tá
get
/ set
phương thức làm suy yếu việc đóng gói (hoặc, nếu bạn không, thì một số lập trình viên khác có thể sẽ về sau ..).
- Nguy cơ đối tượng miền trở nên cồng kềnh với hỗn hợp dữ liệu kinh doanh phức tạp (ví dụ: dữ liệu người dùng nhập qua giao diện người dùng) và trạng thái tạm thời (ví dụ: 'lịch sử' các hành động của người dùng khi đơn hàng đã được sửa đổi).
DDD, OO Design và Plain Model
Một quan niệm sai lầm phổ biến liên quan đến DDD và OO Design là các mô hình "đơn giản" bằng cách nào đó là 'xấu' hoặc 'chống mẫu'. Martin Fowler đã viết một bài báo mô tả Mô hình miền thiếu máu - nhưng khi ông nói rõ trong bài viết, bản thân DDD không nên 'mâu thuẫn' với cách tiếp cận tách biệt giữa các lớp
"Cũng cần nhấn mạnh rằng việc đưa hành vi vào các đối tượng miền không được mâu thuẫn với cách tiếp cận vững chắc của việc sử dụng phân lớp để tách logic miền khỏi những thứ như trách nhiệm trình bày và trách nhiệm trình bày. Logic nên trong đối tượng miền là logic miền - tính hợp lệ , quy tắc kinh doanh - bất cứ điều gì bạn muốn gọi nó. "
Nói cách khác, sử dụng Mô hình đơn giản để giữ dữ liệu doanh nghiệp được chuyển giữa các lớp khác (ví dụ: mô hình Đơn hàng được truyền bởi ứng dụng người dùng khi người dùng muốn tạo đơn hàng mới) không giống với "Mô hình miền thiếu máu". Các mô hình dữ liệu 'đơn giản' thường là cách tốt nhất để theo dõi dữ liệu và truyền dữ liệu giữa các lớp (chẳng hạn như dịch vụ web REST, cửa hàng lưu trữ lâu bền, Ứng dụng hoặc Giao diện người dùng, v.v.).
Logic nghiệp vụ có thể xử lý dữ liệu trong các mô hình đó và có thể theo dõi chúng như một phần của trạng thái kinh doanh - nhưng sẽ không nhất thiết phải sở hữu các mô hình đó.
Rễ tổng hợp
Nhìn một lần nữa tại ví dụ miền Objects - SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
vẫn còn không rõ ràng tổng hợp gốc; nhưng điều đó không thực sự quan trọng bởi vì các Đối tượng Miền này có các trách nhiệm riêng biệt dường như không chồng chéo.
Về mặt chức năng, không cần phải SalesOrderCheckout
nói chuyện với PendingOrdersStream
vì công việc trước đây đã hoàn tất khi nó đã thêm một đơn đặt hàng mới vào Cơ sở dữ liệu; mặt khác, PendingOrdersStream
có thể lấy các đơn đặt hàng mới từ Cơ sở dữ liệu. Các đối tượng này thực sự không cần phải tương tác trực tiếp với nhau (Có lẽ Người hòa giải sự kiện có thể cung cấp thông báo giữa hai đối tượng, nhưng tôi hy vọng mọi khớp nối giữa các đối tượng này sẽ rất lỏng lẻo)
Có lẽ Root tổng hợp sẽ là một IoC Container, đưa một hoặc nhiều đối tượng miền đó vào Bộ điều khiển giao diện người dùng, cũng cung cấp cơ sở hạ tầng khác như EventMediator
và Repository
. Hoặc có lẽ nó sẽ là một loại Dịch vụ Dàn nhạc nhẹ nằm trên cùng của Lớp nghiệp vụ.
Rễ tổng hợp không nhất thiết phải là Đối tượng miền. Vì mục đích giữ Tách biệt các mối quan tâm giữa các đối tượng Miền, nói chung là một điều tốt khi gốc tổng hợp là một đối tượng riêng biệt không có logic nghiệp vụ.