web-dev-qa-db-ja.com

DDD:集約ルートは、子エンティティをリポジトリから削除する責任がありますか?

DDD(ドメイン駆動設計)を使用して大規模なソフトウェアプロジェクトを開発しています。集約ルートとして機能するエンティティがあります。それをRootと呼びましょう。このエンティティは、Childタイプの子エンティティのコレクションを参照します。

このChildエンティティの使用は、私のRootエンティティのクライアントには関係ない実装の詳細であるため、これらの子はRootエンティティによってのみ管理されます。新しい子の削除と既存の子の削除はすべて、Rootエンティティのメソッドに従って行われます。疑似コードは次のようになります

class Root 
{
    private Collection<Child> children;

    public void addChild(ChildParameters params) 
    {
        this.children.append(new Child(params));
    }

    public void removeChildByCriteria(Criteria removalCriteria)
    {
       Child foundChild = this.children.findByCriteria(removalCriteria);
       if (foundChild) {
           this.children.remove(foundChild);
       }
    }
}

オブジェクトモデルだけを見れば、これは非常にクリーンなアプローチだと思います。ただし、これはどこかに永続化する必要があるため、RepositoryRootエンティティとその子をデータベースに格納します。

現在、永続化された子をリポジトリー(したがってデータベース)から削除するロジックの適切な場所を見つけるのに苦労しています。リポジトリの実装に孤立した子を見つけさせ、それらを自動的に削除してもよいですか?または、リポジトリをremoveChildByCriteriaメソッドに渡してルートエンティティ内に明示的にコーディングする必要がある孤立した子ドメインロジックの削除です。

// ...
public void removeChildByCriteria(Criteria removalCriteria, ChildRepositoryInterface childRepository) 
{
    Child foundChild = this.children.findByCriteria(removalCriteria);
    if (foundChild) {
        this.children.remove(foundChild);
        childRepository.remove(foundChild);
    }
}
// ...

または、ルートエンティティ内の階層を管理するロジックを配置するのが間違っているのではなく、ドメインサービスまたはドメインイベントとそれに応じたリスナーを使用する必要がありますか?

3
Subsurf

削除しないでください-しないでください でUdi Dahanの引数を確認してください。

もちろん、それ以来 [〜#〜] gdpr [〜#〜] が物になっています。したがって、ドメイン概念として Forget Me を含めることができます。

リポジトリの実装に孤立した子を見つけさせ、それらを自動的に削除してもよいですか?

それは私には完全に受け入れられるようです。

エバンスは、ブルーブックで、書いています

削除操作では、AGGREGATE境界内のすべてを一度に削除する必要があります。

彼が本のトピックに注意を払っていないことに少しがっかりしています。

EvansはREPOSITORYをメモリ内コレクションの抽象化として説明しています。基本的に、削除操作はキー(識別子)と集約ルートの間のリンクを解除します。これにより、集約ルートがドメインにとらわれない方法で最終的にガベージコレクションされるようになります。

バッキングストアがメモリ内コレクションではなく実際にRDBMSである場合、カスケード削除はかなり合理的な近似です。これは、データストア自体に実装するか、リポジトリ/ ORMロジックに書き込むことができます。

ただし、削除プロトコルでドメイン固有のアクション(他のシステムへのメッセージの送信など)が必要な場合は、そのロジックを表すメソッドを集約内に含める必要があります。アクティブな集約の表現を使用済みの集約の表現に変換することは、ドメインモデルの責任です。

「削除」は、これらのアクションのいずれかまたは両方を合理的に意味する可能性がありますが、それらの責任は明確に分離されています。

[〜#〜]編集[〜#〜]

最初のパスでこれを逃した

リポジトリからの子エンティティ?

子エンティティには通常、独自のリポジトリはありません。エンティティがアグリゲートに属していると判断すると、そのエンティティはアグリゲートのルートに従属します。通常、アグリゲート内のエンティティのallは、ルートのロードに使用されるリポジトリーによってロードされるように配置します。

3
VoiceOfUnreason

Rootオブジェクトは、独自の一貫性を維持する必要があります。つまり、必要に応じてコレクションから子を削除します。

リポジトリは、ルートオブジェクトの永続化を担当します。したがって、リポジトリにルートを追加して再度プルする場合、プルするルートの子は、挿入したルートの子と同じである必要があります。

ここで、選択するかどうかはあなた次第ですWhenルーツを永続化します。 1つの方法は、リポジトリを挿入し、変更のたびにルートを更新することです。しかし、私の見解では、これはやや問題があります。

独自のロジックの外で、それを使用しているアプリまたはサービスでルートを永続化した方がよいでしょう。これにより、オブジェクトの使用方法をより詳細に制御できます。すなわち

_btn_loadDoc()
{
    this.doc = repo.getDoc(id)
}

btn_addParagraph()
{
    this.doc.paragraphs.add(p);
}

btn_abandonChanges()
{
    this.doc = repo.getDoc(id)
}

btn_save()
{
    repo.save(this.doc)
}
_

ここでは、変更を保存しないオプションがあります。リポジトリを挿入してparagraphs.add()で呼び出したかのように、dbで変更を取り消す必要があります。

1
Ewan

リポジトリの実装に孤立した子を見つけさせ、それらを自動的に削除してもよいですか?

はい、それはそれを行うための一般的な方法です。実際には、ORMやカスケードDB制約を削除することで魔法をかけることがよくあります。

消費者の観点から見ると、Repository.Delete(entity)を呼び出した後は、エンティティへの参照を保持する必要はありません。消費者は、子エンティティがどのように処分されるかを知りたくありません。

0
guillaume31