DDD: Tạo các mô-đun có thể tái sử dụng và phân biệt loại dịch vụ (Miền, Cơ sở hạ tầng, Ứng dụng)


8

Vì vậy, sau khi đọc "Triển khai thiết kế hướng tên miền của Vaughn Vernon", tôi đã quyết định cấu trúc lại mã của mình để có thể sử dụng lại tốt hơn bằng cách tách biệt những gì tôi tin là các khái niệm miền cốt lõi thành các mô-đun riêng biệt.

Mỗi mô-đun chứa tập hợp các lớp kiến ​​trúc riêng biệt bao gồm Lớp Miền, Cơ sở hạ tầng và Lớp Ứng dụng / Trình bày (theo khuyến nghị của Vaughn, tôi đã quyết định tách biệt trách nhiệm của lớp Ứng dụng khỏi các tuyến đường, các bộ điều khiển MVC + các mẫu tồn tại trong Lớp trình bày).

Tôi đã quyết định đặt từng lớp trong gói riêng của chúng; và mỗi gói được tham chiếu lớp bên dưới nó như là một phụ thuộc. tức là: Lớp trình bày phụ thuộc vào Lớp ứng dụng, Ứng dụng phụ thuộc vào Cơ sở hạ tầng, v.v. Vì Kho lưu trữ là một phần của Miền, mỗi Giao diện Kho lưu trữ tồn tại trong lớp / gói Miền với trách nhiệm của lớp / gói Cơ sở hạ tầng (Doctrine , Vân vân).

Tôi hy vọng rằng bằng cách cấu trúc lại mã của mình theo cách này, tôi sẽ có thể trao đổi lớp Ứng dụng và sử dụng lại Miền của mình trên nhiều Ứng dụng Web.

Mã cuối cùng trông giống như nó bắt đầu định hình lại tuy nhiên điều vẫn làm tôi bối rối là sự khác biệt giữa Ứng dụng, Cơ sở hạ tầng và Dịch vụ Miền.

Một ví dụ phổ biến của Dịch vụ miền là thứ mà bạn sẽ sử dụng để băm mật khẩu. Điều này có ý nghĩa với tôi từ góc độ SRP vì thực thể Người dùng không nên quan tâm đến nhiều thuật toán băm khác nhau có thể được sử dụng để lưu trữ thông tin đăng nhập của người dùng.

Vì vậy, trong tâm trí đó, tôi đã đối xử với dịch vụ Tên miền mới này giống như Kho lưu trữ của tôi; bằng cách xác định một giao diện trong Miền và để lại việc triển khai đến lớp Cơ sở hạ tầng. Tuy nhiên, bây giờ tôi đang tự hỏi về những gì nên được thực hiện với Dịch vụ ứng dụng.

Hiện tại, mỗi Thực thể có Dịch vụ Ứng dụng riêng, tức là Thực thể Người dùng có Dịch vụ Người dùng trong Lớp Ứng dụng. UserService trong trường hợp này chịu trách nhiệm phân tích các kiểu dữ liệu nguyên thủy và xử lý một trường hợp sử dụng chung "UserService :: CreatUser (tên chuỗi, email chuỗi, v.v.): Người dùng.

Điều tôi quan tâm là thực tế là tôi sẽ cần triển khai lại logic này trên nhiều ứng dụng nếu tôi quyết định trao đổi lớp Ứng dụng. Vì vậy, tôi đoán điều này dẫn tôi đến một vài câu hỏi tiếp theo:

  1. Có phải các Dịch vụ Miền chỉ là một Giao diện tồn tại để cung cấp một lớp trừu tượng giữa Lớp Cơ sở hạ tầng và Mô hình của bạn? tức là: Kho lưu trữ + HashingService, v.v.

  2. Tôi đã đề cập đến việc có một Dịch vụ Ứng dụng trông như thế này:

    • Truy cập / Ứng dụng / Dịch vụ / UserService :: CreatUser (tên chuỗi, email chuỗi, v.v.): Người dùng

    • Chữ ký phương thức chấp nhận các đối số kiểu dữ liệu nguyên thủy và trả về một Thực thể người dùng mới (không phải là DTO!).

    Điều này có thuộc về lớp Cơ sở hạ tầng như là một triển khai của một số giao diện được xác định trong lớp Miền hoặc trên thực tế là Lớp ứng dụng phù hợp hơn do các đối số kiểu dữ liệu nguyên thủy, v.v. ?

    thí dụ:

    Access/Domain/Services/UserServiceInterface 

    Access/Infrastructure/Services/UserService implements UserServiceInterface
  3. Làm thế nào các mô-đun riêng biệt nên xử lý các mối quan hệ đơn hướng. Lớp ứng dụng của mô-đun tham chiếu B (như hiện tại) hay triển khai cơ sở hạ tầng (thông qua giao diện riêng biệt)?

  4. Các dịch vụ lớp ứng dụng có yêu cầu một giao diện riêng không? Nếu câu trả lời là có thì chúng nên được đặt ở đâu?


2
Đây là những vấn đề rất đa dạng. Tôi đề nghị bạn chia điều này thành các câu hỏi riêng biệt.
guillaume31

Câu trả lời:


7

Có phải các Dịch vụ Miền chỉ là một Giao diện tồn tại để cung cấp một lớp trừu tượng giữa Lớp Cơ sở hạ tầng và Mô hình của bạn? tức là: Kho lưu trữ + HashingService, v.v.

Trách nhiệm dịch vụ tên miền bao gồm một số điều. Rõ ràng nhất là logic nhà ở không phù hợp với một thực thể duy nhất. Ví dụ: bạn có thể cần ủy quyền hoàn tiền cho một giao dịch mua nhất định, nhưng để hoàn thành thao tác bạn cần dữ liệu từ Purchasethực thể, Customerthực thể, CustomerMembershipthực thể.

Các dịch vụ miền cũng cung cấp các hoạt động cần thiết cho miền để hoàn thành chức năng của nó PasswordEncryptionService, nhưng việc triển khai dịch vụ này sẽ nằm trong lớp cơ sở hạ tầng vì đây chủ yếu sẽ là một giải pháp kỹ thuật.

Dịch vụ cơ sở hạ tầng là các dịch vụ hoạt động cơ sở hạ tầng như mở kết nối mạng, sao chép tệp từ hệ thống tệp, nói chuyện với dịch vụ web bên ngoài hoặc nói chuyện với cơ sở dữ liệu.

Dịch vụ ứng dụng là việc thực hiện trường hợp sử dụng trong ứng dụng bạn đang xây dựng. Nếu bạn đang hủy đặt chỗ chuyến bay, bạn sẽ:

  1. Truy vấn cơ sở dữ liệu cho đối tượng Đặt trước.
  2. gọi Đặt chỗ-> hủy bỏ.
  3. Lưu đối tượng vào DB.

Lớp ứng dụng là máy khách của miền. Tên miền không có ý tưởng gì về trường hợp sử dụng của bạn. Nó chỉ hiển thị các chức năng thông qua các dịch vụ tổng hợp và tên miền của nó. Tuy nhiên, lớp ứng dụng phản ánh những gì bạn đang cố gắng đạt được bằng cách sắp xếp các lớp cơ sở hạ tầng và miền.

Tôi đã đề cập đến việc có một Dịch vụ ứng dụng trông như thế này: Access / Application / Services / UserService :: CreatUser (tên chuỗi, email chuỗi, v.v.): Người dùng Chữ ký phương thức chấp nhận các đối số kiểu dữ liệu nguyên thủy và trả về một Thực thể người dùng mới (không phải là DTO !).

PHP có thể không phải là nơi tốt nhất để bắt đầu tìm hiểu về DDD vì nhiều khung công tác PHP ngoài kia (Laravel, Symfony, Zend, .. vv) có xu hướng thúc đẩy RAD. Họ tập trung nhiều hơn vào CRUD và dịch các biểu mẫu cho các thực thể. CRUD! = DDD

Lớp trình bày của bạn phải chịu trách nhiệm đọc các đầu vào biểu mẫu từ đối tượng yêu cầu và gọi dịch vụ ứng dụng chính xác. Dịch vụ ứng dụng sẽ tạo người dùng và gọi kho lưu trữ Người dùng để lưu người dùng mới. Bạn có thể tùy ý đưa DTO của người dùng trở lại lớp trình bày.

Làm thế nào các mô-đun riêng biệt nên xử lý các mối quan hệ đơn hướng. Lớp ứng dụng của mô-đun tham chiếu B (như hiện tại) hay triển khai cơ sở hạ tầng (thông qua giao diện riêng biệt)?

Mô-đun từ trong biệt ngữ DDD có một ý nghĩa khác với những gì bạn đang mô tả. Một mô-đun nên chứa các khái niệm liên quan. Ví dụ, một mô-đun đặt hàng trong lớp miền có thể chứa tập hợp Đơn hàng, thực thể OrderItem, OrderRep repositoryInterface và MaxOrderValidationService.

Một mô-đun Đặt hàng trong lớp ứng dụng có thể chứa OrderApplicationServie, CreateOrderCommand và OrderDto.

Nếu bạn đang nói về các lớp thì mỗi lớp tốt nhất nên phụ thuộc vào giao diện của các lớp khác bất cứ khi nào có thể. Lớp trình bày nên phụ thuộc vào giao diện của lớp ứng dụng. Lớp ứng dụng nên tham chiếu các giao diện của kho lưu trữ hoặc dịch vụ miền.

Cá nhân tôi không tạo giao diện cho các thực thể và các đối tượng giá trị vì tôi tin rằng các giao diện nên liên quan đến một hành vi, nhưng YMMV :)

Các dịch vụ lớp ứng dụng có yêu cầu một giao diện riêng không? Nếu câu trả lời là có thì chúng nên được đặt ở đâu?

Nó phụ thuộc :) cho các ứng dụng phức tạp Tôi xây dựng giao diện vì chúng tôi áp dụng thử nghiệm đơn vị, tích hợp và chấp nhận nghiêm ngặt. Khớp nối lỏng lẻo là chìa khóa ở đây và các giao diện nằm trong cùng một lớp (lớp ứng dụng).

Đối với ứng dụng đơn giản, tôi xây dựng dựa trên các dịch vụ ứng dụng trực tiếp.


Tôi đồng ý với hầu hết những gì bạn đã viết, chỉ là tôi muốn nói rằng phần RAD của các khung công tác giúp rất nhiều cho việc tạo nguyên mẫu cho ứng dụng, để tách rời chúng ta có thể dựa vào Mô hình miền ngay cả khi PHP không biết điều này, nếu chúng ta không biết có một bản ghi hoạt động như lớp nguồn dữ liệu, chúng ta có thể coi nó là DTO như chú bob đã nói là một loại DTO khác và một bộ chuyển đổi có thể làm trung gian giữa lớp miền và các lệnh nguồn dữ liệu cũng là một loại DTO khác, cách tốt nhất là ghép đôi biểu mẫu cho Command (DTO) không phải là thực thể, hầu hết mọi khung công tác đều có DI container nên sử dụng giao diện.
Cherif BOUCHELAGHEM

1

Hmm trả lời ngắn gọn cho một câu hỏi dài, nhưng tôi thấy mô hình như sau

Quy tắc 1: Đối tượng miền nên có một gốc tổng hợp duy nhất

Quy tắc 2: Rễ tổng hợp không nên quá lớn, Chia mọi thứ thành các bối cảnh giới hạn

Vấn đề: Rễ tổng hợp sớm trở nên quá lớn và không có cách rõ ràng nào để vẽ một đường thẳng giữa các Mô hình miền khác nhau trong chúng

Giải pháp: Dịch vụ tên miền. Tạo giao diện mà bạn có thể đưa vào Mô hình miền cho phép họ thực hiện những việc bên ngoài Bối cảnh miền hoặc Root tổng hợp.

Vì vậy, tôi nghĩ rằng tôi sẽ nói rằng các ví dụ của bạn chỉ là các Dịch vụ / Kho lưu trữ thông thường, v.v., IDatabaseRep repositoryForStoringUsers hoặc IGenericHashingCode

Dịch vụ miền cho phép liên lạc giữa các bối cảnh bị ràng buộc. I E

User.UpgradeAccount(IAccountService accountService)
{
    accountService.UpgradeUsersAccount(this.Id);
}

Trong đó Người dùng và Tài khoản nằm trong các Bối cảnh Tổng hợp / Bối cảnh riêng biệt.

Nếu Người dùng và Tài khoản ở cùng một gốc Tổng hợp, tất nhiên bạn có thể thực hiện:

User.UpgradeAccount()
{
    this.MyAccount.Upgrade();
}

Tôi không hoàn toàn rõ ràng từ câu hỏi của bạn về cách bạn tích hợp công cụ Ứng dụng / Cơ sở hạ tầng nTier và công cụ Mô-đun. Obvs bạn không thực sự muốn bất kỳ tham chiếu chéo nào giữa các Bối cảnh bị ràng buộc, vì vậy bạn sẽ đặt Giao diện dịch vụ miền của mình trong Mô-đun riêng của chúng mà không tham chiếu bất kỳ Bối cảnh bị ràng buộc nào khác. giới hạn bạn chỉ hiển thị các loại giá trị cơ sở hoặc chỉ DTOs perhapse


Tích hợp là mối quan tâm của gói / lớp Ứng dụng của mỗi mô-đun. tức là: Service Container Dynamic Bindings + Routes. Đối với các mối quan hệ mô-đun chéo - nếu một mô-đun tách biệt khác (ví dụ: Quản lý dự án) cần thực hiện một số loại Xác thực trong API tương ứng của chính nó thì tôi bao gồm lớp Ứng dụng của mô-đun "Truy cập" làm phụ thuộc được liệt kê. Trình quản lý gói sau đó lấy bất kỳ phụ thuộc lồng nhau nào còn lại. Ứng dụng truy cập -> Cơ sở hạ tầng truy cập -> Truy cập tên miền để tôi có thể thực hiện các công việc cần thiết.
dùng2308097

1
Có, tôi nghĩ rằng loại liên kết tên miền chéo có lẽ là một sai lầm. Bạn có thể nhận được phụ thuộc vòng tròn
Ewan
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.