14

DDD について学んだことを適用しようとしていますが、ドメイン モデルの依存関係の流れについて少し混乱しています。

私の質問は次のとおりです。

  1. エンティティは、ドメイン内のファクトリ、リポジトリ、サービスを認識する必要がありますか?
  2. リポジトリはドメイン内のサービスを認識する必要がありますか?

もう 1 つ頭を悩ませているのは、コレクションにエンティティを追加したい場合にコレクションをどのように処理するかということです。

私が単純な CMS を開発しているとしましょう。CMS には、タグ エンティティを含む記事エンティティとタグ コレクションがあります。

さて、新しいタグでリレーションを追加したい場合に備えて。それを行うためのより良い方法は何ですか?(PHPでの例)

$article->tags->add(TagEntity);
$articleRepository->save($article);

または私はサービスでそれを行うことができます。

$articleService->addTag($article, TagEntity);

どう思いますか?

ありがとう。

4

5 に答える 5

29

エンティティと値オブジェクトは、相互に依存することを除いて、何にも依存するべきではありません。これらは、 DDD のすべてのビルディング ブロックの中で最も基本的なものです。それらは問題領域の概念を表しているため、問題に焦点を当てる必要があります。それらをファクトリー、リポジトリ、およびサービスに依存させることで、その焦点をぼかすことができます。エンティティと値オブジェクトでサービスへの参照を持つことには、別の問題があります。サービスもドメイン ロジックを所有しているため、ドメイン モデルの責任の一部をサービスに委譲したくなり、最終的に貧血ドメイン モデルにつながる可能性があります。

ファクトリとリポジトリは、エンティティの作成と永続化に使用される単なるヘルパーです。ほとんどの場合、実際の問題ドメインに類推がないため、ファクトリとリポジトリからサービスとエンティティへの参照を持つことは、ドメイン ロジックによると意味がありません。

あなたが提供した例に関して、これは私がそれを実装する方法です

$article->addTag($tag);
$articleRepository->save($article);

基になるコレクションへの直接アクセスは許可しません。コレクションに追加する前にArticle、ドメイン ロジック (制約の適用、検証) を実行する必要があるからです。Tag

これを避ける

$articleService->addTag($article, $tag);

サービスは、概念的にどのエンティティにも属さない操作を実行する場合にのみ使用してください。まず、それをエンティティに適合させようとし、どのエンティティにも適合しないことを確認します。その場合にのみ、サービスを使用します。この方法では、貧血ドメイン モデルで終わることはありません。

更新 1

Eric Evans の「Domain-Driven Design: Tackling Complexity in the Heart of Software」の本からの引用:

サービスは慎重に使用する必要があり、ENTITIES と VALUE OBJECTS のすべての動作を取り除くことはできません。

更新 2

誰かがこの回答に反対票を投じましたが、その理由はわかりません。理由を疑うしかありません。エンティティとサービスの間の参照に関するものであったり、サンプル コードに関するものであったりします。サンプル コードについては、私自身の経験に基づいた私の意見であるため、多くのことを行うことはできません。しかし、私は参照部分についてさらに調査を行い、ここに私が思いついたものがあります.

エンティティからサービス、リポジトリ、ファクトリを参照するのは得策ではないと考えているのは私だけではありません。SOで同様の質問を見つけました:

この主題に関するいくつかの優れた記事もあります。特に、このHow not to inject services in entitiesは、 Double Dispatchという名前のエンティティからサービスを必死に呼び出す必要がある場合の解決策も示しています。PHP に移植された記事の例を次に示します。

interface MailService
{
    public function send($sender, $recipient, $subject, $body);
}

class Message
{
    //...
    public function sendThrough(MailService $mailService)
    {
        $subject = $this->isReply ? 'Re: ' . $this->title : $this->title;
        $mailService->send(
            $this->sender, 
            $this->recipient, 
            $subject, 
            $this->getMessageBody($this->content)
        );
    }
}

MailServiceしたがって、ご覧のとおり、エンティティ内のサービスへの参照はありませんMessage。代わりに、エンティティのメソッドに引数として渡されます。この記事「DDD: サービス」の著者 ( http://devlicio.us/のコメント セクション) によって、同じ解決策が提案されています。

また、Eric Evans が著書「ドメイン駆動設計: ソフトウェアの心臓部における複雑さへの取り組み」でこれについて述べていることも調べてみました。簡単に検索したところ、正確な答えは見つかりませんでしたが、エンティティが実際にサービスを静的に呼び出す例を見つけました。つまり、エンティティへの参照はありません。

public class BrokerageAccount {
    String accountNumber;
    String customerSocialSecurityNumber;

    // Omit constructors, etc.

    public Customer getCustomer() {
        String sqlQuery =
            "SELECT * FROM CUSTOMER WHERE" +
            "SS_NUMBER = '" + customerSocialSecurityNumber + "'";
        return QueryService.findSingleCustomerFor(sqlQuery);
    }

    public Set getInvestments() {
        String sqlQuery =
            "SELECT * FROM INVESTMENT WHERE" +
            "BROKERAGE_ACCOUNT = '" + accountNumber + "'";
        return QueryService.findInvestmentsFor(sqlQuery);
    }
}

そして、下のメモには次のように記載されています。

注: データベースから行をフェッチしてオブジェクトを作成するためのユーティリティである QueryService は、例を説明するのは簡単ですが 、実際のプロジェクトでは必ずしも適切な設計ではありません。

上記の DDDSample プロジェクトのソース コードを見ると、エンティティはmodelパッケージ内のオブジェクト、つまりエンティティと値オブジェクト以外には何も参照していないことがわかります。ちなみに、DDDSampleプロジェクトは「ドメイン駆動設計:ソフトウェアの中心にある複雑さへの取り組み」という本で詳しく説明されています...

また、あなたと共有したいもう 1 つのことは、 domaindrivendesign Yahoo Groupでの同様の議論です。ディスカッションからのこのメッセージは、リポジトリを参照するモデル オブジェクトに関する Eric Evans の言葉を引用しています。

結論

要約すると、エンティティからサービス、リポジトリ、およびファクトリへの参照を持つことは適切ではありません。これは最も受け入れられている意見です。リポジトリとファクトリはドメイン層の市民ですが、問題のドメインの一部ではありません。場合によっては (たとえば、DDD に関するウィキペディアの記事で) ドメイン サービスの概念はPure Fabricationと呼ばれ、クラス (サービス) が「問題のドメインの概念を表していない」ことを意味します。Eric Evans は彼の著書でサービスの概念について別のことを言っているので、ファクトリとリポジトリを純粋なファブリケーションと呼びたいと思います。

しかし、操作が実際に重要なドメイン概念である場合、サービスはモデル駆動設計の自然な部分を形成します。実際には何も表さない偽のオブジェクトとしてではなく、サービスとしてモデルで宣言されているため、スタンドアロン操作は誰も誤解を招くことはありません。

上記によると、あなたのエンティティからサービスを呼び出すことは、時には正気なことかもしれません。次に、エンティティ クラスでサービスへの参照を保持する必要がないように、ダブル ディスパッチ アプローチを使用できます。

もちろん、エンティティからのドメイン サービスへのアクセスに関する記事の著者のように、主流の意見に反対する人も常にいます。

于 2012-12-25T11:02:45.960 に答える
3

エンティティは、ドメイン内のファクトリ、リポジトリ、サービスを認識する必要がありますか?

  • アプリケーション サービス:いいえ
  • ドメイン サービス:あり(ドメイン層にあるため)
  • 工場:はい、ドメイン層にあるため
  • リポジトリ インターフェイス:はい、ドメイン層にあるため
  • リポジトリの実装:いいえ、インフラストラクチャ層にあるため

インターフェイスと実装の違いに注意してください。これが、インターフェイスと実装を使用する必要がある理由です。

参考までに、ファクトリとリポジトリはサービスなので、次のように一般化できます。

  • サービス インターフェイス:ドメイン層にある場合ははい

簡単

ドメイン サービスは、ドメイン レイヤー内で定義されるサービスですが、実装はインフラストラクチャ レイヤーの一部である場合があります。リポジトリは、実装が実際にインフラストラクチャ レイヤーにあるドメイン サービスであり、ファクトリも、実装が通常ドメイン レイヤー内にあるドメイン サービスです。

(出典: http://www.methodsandtools.com/archive/archive.php?id=97p2 )

リポジトリはドメイン内のサービスを認識する必要がありますか?

通常はいいえ、結局なぜですか?リポジトリは永続性を管理します。しかし、インフラ層(永続性)はドメイン層を知っているので、それは「禁止」ではないと思います。

もう 1 つ頭を悩ませているのは、コレクションにエンティティを追加したい場合にコレクションをどのように処理するかということです。

可能な場合は OOP アプローチを優先します。

$article = new Article();
$article->addTag($tag);
$articleRepository->save($article);

私はもっ​​と理にかなっています。

ドメイン サービスは、エンティティ内に簡単には存在しないビジネス ロジックです。

(http://www.methodsandtools.com/archive/archive.php?id=97p2)

またはまた:

ドメイン内の重要なプロセスまたは変換が ENTITY または VALUE OBJECT の自然な責任ではない場合、サービスとして宣言されたスタンドアロン インターフェイスとしてモデルに操作を追加します。

(エリック・エヴァンス)

要約すると、必要に応じてドメイン サービスを作成します。これは自動ではありません。

于 2012-12-27T16:19:53.557 に答える
1

正しい答えがあるとは思わないという前提でこれに答えます。アプローチが異なるだけです。

ドメイン オブジェクトの観点から考えると、最初のアプローチは DDD です。純粋にドメイン オブジェクトを扱っています。

ただし、これを API/サービス レイヤーの一部として公開するサービス オブジェクトの用途はあると思います。この場合、#1 のコードをサービス #2 内にラップします。これにより、ドメイン オブジェクトを外部のコンシューマーに公開することを回避できます。また、ドメイン モデルを更新している間、外部インターフェイス/API を変更しないで維持できます。

ただし、これは 1 つの意見にすぎません。

于 2012-12-24T11:15:35.540 に答える
0

まず第一に、それに夢中にならないでください。不必要なサービス クラスなどを作成してオーバー エンジニアリングを行うのは簡単です。コードが少ないのは良いことです。 元のソース資料と、 JavaまたはC#のコードを確認してください。

1) エンティティは、ドメイン内のファクトリ、リポジトリ、サービスを認識する必要がありますか?

必要に応じて可能です。たとえば、注釈が付けられた私の(Java)クラスに@Entityも注釈を付け@Configurableて、セッション/他のクラスを注入することができます。必要なすべてのビジネス ロジックをカプセル化し、1 つのドメイン クラスにある明確でシンプルな API を公開することがポイントです。

2) リポジトリはドメイン内のサービスを認識する必要がありますか?

いいえ。しかし、逆の可能性があります。サービスはリポジトリを使用します。

サービスは、複数のドメイン オブジェクト/エンティティ/ルート集合体が使用される場合に使用されます。したがって、TAG が別のエンティティであると仮定すると、これで問題ありません。

$articleService->addTag($article, TagEntity); 

ただし、タグが別のルート集約ではない場合は、次のようにすることができます

$article->tags->add(TagEntity);

また、記事自体は、その中にリポジトリ/dao を挿入することで (他の呼び出しなしで) 保存を行います。

于 2012-12-28T13:07:02.507 に答える