ドメイン駆動設計-エンティティ問題の外部依存関係


23

Domain-Driven-Designを開始したいのですが、開始する前に解決したい問題がいくつかあります:)

グループとユーザーがあり、ユーザーがグループに参加したい場合、groupsService.AddUserToGroup(group, user)メソッドを呼び出していると想像してください。DDDで行う必要がgroup.JoinUser(user)あります。

ユーザーを追加するための検証ルールがある場合、またはユーザーがグループに追加されたときに外部タスクを開始する必要がある場合に問題が発生します。これらのタスクを実行すると、エンティティが外部依存関係を持つことになります。

例としては、ユーザーが最大3グループまでしか参加できないという制限があります。これを検証するには、group.JoinUserメソッド内からのDB呼び出しが必要です。

しかし、エンティティがいくつかの外部サービス/クラスに依存しているという事実は、私にはそれほど自然で「自然」ではないようです。

DDDでこれに対処する適切な方法は何ですか?

回答:


15

グループとユーザーがあり、ユーザーがグループに参加したいときに、groupsService.AddUserToGroup(group、user)メソッドを呼び出しているとします。DDDでは、group.JoinUser(user)を実行する必要がありますが、これは非常に適切です。

ただし、DDDでは、手元のタスクが複雑すぎる場合やエンティティモデルに適合しない場合は、(ステートレス)サービスを使用してタスクを実行することもお勧めします。ドメイン層にサービスを配置しても構いません。ただし、ドメイン層のサービスにはビジネスロジックのみを含める必要があります。一方、外部タスクとアプリケーションロジック(電子メールの送信など)では、アプリケーションレイヤーのドメインサービスを使用する必要があります。たとえば、別の(アプリケーション)サービスでラップすることができます。

ユーザーを追加するための検証ルールがある場合、問題が表示されます...

検証ルールはドメインモデルに属します!ドメインオブジェクト(エンティティなど)内にカプセル化する必要があります。

...または、ユーザーがグループに追加されたときにいくつかの外部タスクを開始する必要があります。これらのタスクを持つことにより、エンティティが外部依存関係を持つことになります。

あなたが話している外部タスクの種類はわかりませんが、それはメールの送信などのようなものだと思います。しかし、これは実際にはドメインモデルの一部ではありません。それはアプリケーション層に存在し、私見でそこに保持されるべきです。ドメイン層のサービスとエンティティを操作して、これらのタスクを実行するサービスをアプリケーション層に持つことができます。

しかし、エンティティがいくつかの外部サービス/クラスに依存しているという事実は、私にはそれほど自然で「自然」ではないようです。

それは不自然であり、起こるべきではありません。エンティティは、その責任ではないものについて知るべきではありません。エンティティの相互作用を調整するには、サービスを使用する必要があります。

DDDでこれに対処する適切な方法は何ですか?

あなたの場合、関係はおそらく双方向であるべきです。ユーザーがグループに参加するか、グループがユーザーを取得するかは、ドメインによって異なります。ユーザーはグループに参加していますか?または、ユーザーはグループに追加されますか?ドメインでどのように機能しますか?

とにかく、双方向の関係があるため、ユーザー集計内でユーザーが既に属しているグループの数を決定できます。ユーザーをグループに渡すか、グループをユーザーに渡すかは、責任のあるクラスを決定した後は技術的に簡単です。

検証は、エンティティによって実行される必要があります。全体は、電子メールの送信などの技術的なこともできるアプリケーション層のサービスから呼び出されます。

ただし、検証ロジックが本当に複雑な場合は、ドメインサービスの方が優れたソリューションになる可能性があります。その場合は、そこにビジネスルールをカプセル化し、アプリケーション層から呼び出します。


しかし、あまり多くのロジックをエンティティの外部に移動する場合、内部に何を保持する必要がありますか?
SiberianGuy

エンティティの直接的な責任!たとえば、「ユーザーがグループに参加できる」と言うことができる場合、それはユーザーエンティティの責任です。技術的な理由でトレードオフの決定をしなければならない場合があります。私も双方向の関係の大ファンではありませんが、時にはモデルに最適です。そのため、ドメインについて話すときには注意深く耳を傾けてください。「エンティティは...」「エンティティは...」このような文を聞くと、それらの操作はエンティティに属している可能性が高いです。
ファルコン

さらに、2つ以上の関連のないオブジェクトが何かを達成するためにタスクに参加する必要がある場合、サービスが必要であることを知っています。
ファルコン

1
答えてくれてありがとう、ファルコン!ところで、私は常にステートレスサービスを使用しようとしたため、DDDに一歩近づいています:)ドメインで、このUserJoinsToGroup操作がGroupに属しているとしましょう。問題は、その操作を検証するために、ユーザーが既に参加しているグループの数を知る必要があることです(既に3を超える操作を拒否するため)。データベースを照会する必要があることを知るため。グループエンティティからどのように行うことができますか?エンティティに自然に属するはずの操作でDBに触れる必要がある場合、いくつかの例があります(必要に応じて投稿します:))
Shaddix

2
考えてみると、GroupMembershipエンティティはどうですか?ファクトリーによって構築でき、このファクトリーはリポジトリーにアクセスできます。それは良いDDDであり、メンバーシップの作成をカプセル化します。ファクトリはリポジトリにアクセスし、メンバーシップを作成してから、それをユーザーとグループにそれぞれ追加します。この新しいエンティティは、特権をカプセル化することもできます。たぶんそれは良い考えです。
ファルコン

3

検証の問題に取り組む方法はMembershipService次のとおりです。

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

Groupエンティティにを注入する必要がありますIMemberShipService。これは、クラスレベルまたはメソッドレベルで実行できます。メソッドレベルで行うと仮定しましょう。

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
   }
}

アプリケーションサービス:ConstructorインジェクションGroupServiceIMemberShipService使用してインジェクトできJoinUserGroupクラスのメソッドに渡すことができます。


1
あなたは検討する必要があります書式を読みやすくするためにあなたのポストでのソースコードを
ベニ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.