Thiết kế hướng tên miền - phụ thuộc bên ngoài trong vấn đề Thực thể


22

Tôi muốn bắt đầu Thiết kế hướng tên miền, nhưng có một số vấn đề tôi muốn giải quyết trước khi bắt đầu :)

Hãy tưởng tượng tôi có một Nhóm và Người dùng và khi người dùng muốn tham gia một nhóm, tôi đang gọi groupsService.AddUserToGroup(group, user)phương thức. Trong DDD tôi nên làm group.JoinUser(user), có vẻ khá tốt.

Vấn đề xuất hiện nếu tôi có một số quy tắc xác thực để thêm người dùng hoặc một số tác vụ bên ngoài cần được bắt đầu khi người dùng được thêm vào nhóm. Có những nhiệm vụ này sẽ dẫn đến thực thể có phụ thuộc bên ngoài.

Một ví dụ có thể là - một hạn chế mà người dùng chỉ có thể tham gia tối đa 3 nhóm. Điều này sẽ yêu cầu các cuộc gọi DB từ bên trong nhóm.JoinUser phương thức để xác thực điều này.

Nhưng thực tế là một Thực thể phụ thuộc vào một số dịch vụ / lớp học bên ngoài có vẻ không tốt và "tự nhiên" đối với tôi.

Cách thích hợp để giải quyết vấn đề này trong DDD là gì?

Câu trả lời:


15

Hãy tưởng tượng tôi có một Nhóm và Người dùng và khi người dùng muốn tham gia một nhóm, tôi đang gọi phương thức của nhómService.AddUserTogroup (nhóm, người dùng). Trong DDD tôi nên làm nhóm.JoinUser (người dùng), trông khá tốt.

Nhưng DDD cũng khuyến khích bạn sử dụng các dịch vụ (không trạng thái) để thực hiện các nhiệm vụ, nếu nhiệm vụ trong tay quá phức tạp hoặc không phù hợp với mô hình thực thể. Bạn có thể có các dịch vụ trong lớp miền. Nhưng các dịch vụ trong lớp miền chỉ nên bao gồm logic nghiệp vụ. Mặt khác, các tác vụ bên ngoài và logic ứng dụng (như gửi email), nên sử dụng dịch vụ miền trong lớp ứng dụng, trong đó bạn có thể có một dịch vụ (ứng dụng-) riêng biệt.

Vấn đề xuất hiện nếu tôi có một số quy tắc xác thực để thêm người dùng ...

Các quy tắc xác nhận không thuộc về mô hình miền! Chúng nên được gói gọn bên trong các đối tượng miền (thực thể, v.v.).

... hoặc một số tác vụ bên ngoài cần được bắt đầu khi người dùng được thêm vào nhóm. Có những nhiệm vụ này sẽ dẫn đến thực thể có phụ thuộc bên ngoài.

Mặc dù tôi không biết bạn đang nói về loại nhiệm vụ bên ngoài nào, tôi cho rằng nó giống như gửi email, v.v. Nhưng đây không thực sự là một phần của mô hình miền của bạn. Nó sẽ sống trong lớp ứng dụng và được hanlded ở đó imho. Bạn có thể có một dịch vụ trong lớp ứng dụng hoạt động trên các dịch vụ và thực thể miền để thực hiện các tác vụ đó.

Nhưng thực tế là một Thực thể phụ thuộc vào một số dịch vụ / lớp học bên ngoài có vẻ không tốt và "tự nhiên" đối với tôi.

Điều đó là không tự nhiên và không nên xảy ra. Các thực thể không nên biết về những thứ không phải là trách nhiệm của nó. Dịch vụ nên được sử dụng để phối hợp các tương tác thực thể.

Cách thích hợp để giải quyết vấn đề này trong DDD là gì?

Trong trường hợp của bạn, mối quan hệ có lẽ nên là hai chiều. Việc người dùng tham gia nhóm hay nhóm lấy người dùng tùy thuộc vào tên miền của bạn. Người dùng có tham gia nhóm không? Hoặc là người dùng được thêm vào một nhóm? Làm thế nào nó hoạt động trong miền của bạn?

Dù sao, bạn có mối quan hệ hai chiều và do đó có thể xác định số lượng nhóm người dùng đã thuộc về tổng hợp người dùng. Cho dù bạn chuyển người dùng cho nhóm hoặc nhóm cho người dùng về mặt kỹ thuật là một khi bạn đã xác định được lớp chịu trách nhiệm.

Việc xác nhận nên được thực hiện bởi các thực thể. Toàn bộ điều được gọi từ một dịch vụ của lớp ứng dụng cũng có thể thực hiện các công cụ kỹ thuật, như gửi email, v.v.

Tuy nhiên, nếu logic xác thực thực sự phức tạp, một dịch vụ miền có thể là một giải pháp tốt hơn. Trong trường hợp đó, đóng gói các quy tắc kinh doanh trong đó và sau đó gọi nó từ lớp ứng dụng của bạn.


Nhưng nếu chúng ta di chuyển quá nhiều logic bên ngoài thực thể, thì nên giữ gì bên trong?
SiberianGuy

Trách nhiệm trực tiếp của đơn vị! Nếu bạn có thể nói "người dùng có thể tham gia một nhóm" chẳng hạn, thì đó là trách nhiệm của thực thể người dùng. Đôi khi bạn phải đưa ra quyết định đánh đổi vì lý do kỹ thuật. Tôi cũng không phải là một fan hâm mộ lớn của các mối quan hệ hai chiều, nhưng đôi khi nó phù hợp nhất với mô hình. Vì vậy, hãy lắng nghe cẩn thận khi nói về tên miền. "Một thực thể làm ..." "Thực thể có thể ..." Khi bạn nghe những câu như vậy, thì những hoạt động đó rất có thể thuộc về thực thể.
Falcon

Hơn nữa, bạn biết bạn cần một dịch vụ khi hai hoặc nhiều đối tượng không liên quan khác cần tham gia vào một nhiệm vụ để hoàn thành một việc gì đó.
Falcon

1
Cảm ơn câu trả lời của bạn, Falcon! Btw, tôi luôn cố gắng sử dụng các dịch vụ không trạng thái, vì vậy tôi tiến một bước gần hơn với DDD :) Hãy nói rằng trong một miền, hoạt động UserJoinsTogroup này thuộc về Nhóm. Vấn đề là, để xác thực hoạt động đó tôi cần biết trong đó có bao nhiêu nhóm mà Người dùng đã tham gia (để từ chối một hoạt động nếu nó đã> 3). Để biết rằng tôi cần truy vấn cơ sở dữ liệu. Làm thế nào tôi có thể làm điều đó từ thực thể nhóm? Tôi có thêm một số ví dụ, khi tôi cần chạm vào DB trong các hoạt động nên thuộc về thực thể (tôi sẽ đăng chúng nếu cần :))
Shaddix

2
Chà, nếu tôi nghĩ về nó: Thế còn một thực thể GroupMembership thì sao? Nó có thể được xây dựng bởi một nhà máy và nhà máy này có thể truy cập vào kho. Đó sẽ là DDD tốt và gói gọn việc tạo thành viên. Nhà máy có thể truy cập Kho lưu trữ, tạo Tư cách thành viên và hơn thế là thêm nó vào người dùng và nhóm tương ứng. Thực thể mới này cũng có thể gói gọn các đặc quyền. Có lẽ đó là một ý tưởng tốt.
Falcon

3

Cách tôi sẽ tiếp cận vấn đề xác thực là theo cách này: Tạo một Dịch vụ Miền được gọi là MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

Các thực thể nhóm cần được tiêm IMemberShipService. Điều này có thể được thực hiện ở cấp độ lớp hoặc cấp độ phương thức. Giả sử chúng ta làm điều đó ở cấp độ phương thức.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

Dịch vụ ứng dụng: GroupServicecó thể được tiêm bằng IMemberShipServicecách sử dụng hàm Con Contor, sau đó nó có thể chuyển sang JoinUserphương thức của Grouplớp.


1
Bạn có thể muốn xem xét định dạng mã nguồn trong bài đăng của mình để dễ đọc
Benni
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.