web-dev-qa-db-ja.com

これらのすべてのサービスを使用して、どうすれば貧血にならないのですか?

委任とビジネスロジックのカプセル化の間の境界をどこに描画しますか?委任すればするほど 貧血 になるように思えます。ただし、委任は再利用とDRYプリンシパルを促進します。それで、委任に適切なものとドメインモデルに残すべきものは何ですか?

例として次の懸念を取り上げます。

承認。ドメインオブジェクトがそのアクセス制御ルール(CanEditプロパティなど)の維持を担当するのか、それともアクセスの管理のみを担当する別のコンポーネント/サービス(たとえば、 IAuthorizationService.CanEdit(object)?それとも2つの組み合わせでなければなりませんか?おそらく、ドメインオブジェクトには、実際の作業を実行するために内部IAuthorizationServiceに委任するCanEditプロパティがありますか?

検証。上記と同じ議論が検証に関係しています。誰がルールを維持し、誰がそれらを評価する責任がありますか?一方で、オブジェクトの状態はそのオブジェクトに属している必要があり、有効性は状態ですが、すべてのドメインオブジェクトのルールを評価するために使用されるコードを書き直したくありません。この場合、継承を使用することができます......

オブジェクトの作成。ファクトリクラスとファクトリメソッド、またはインスタンスの「更新」。別のファクトリクラスを使用する場合、作成ロジックを分離してカプセル化できますが、オブジェクトの状態をファクトリに開放することを犠牲にします。これは、ドメインレイヤーが別のアセンブリにある場合、ファクトリで使用される内部コンストラクターを公開することで管理できますが、複数の作成パターンがある場合は問題になります。そして、すべてのファクトリーが適切なコンストラクターを呼び出すだけである場合、ファクトリーを持つ意味は何ですか?

クラスのファクトリメソッドは、オブジェクトの内部状態を開くことによる問題を排除しますが、それらは静的であるため、別個のファクトリクラスの場合のように、ファクトリインターフェースの挿入によって依存関係を壊すことはできません。

永続性。ドメインオブジェクトがCanEditを公開する一方で、承認チェックを実行する責任を別の当事者(IAuthorizationService)に委任する場合、同じことを行うドメインオブジェクトにSaveメソッドがないのはなぜでしょうか。これにより、オブジェクトの内部状態を評価して、カプセル化を解除せずに操作を実行できるかどうかを判断できます。もちろん、リポジトリインスタンスをドメインオブジェクトに注入する必要があります。これにより、少し匂いがするので、代わりにドメインイベントを発生させ、ハンドラーが永続化操作を実行できるようにしますか?

これでどこへ行くのかわかりますか?

Rockford Lhotkaは、CSLAフレームワークのClass-in-Chargeルートを採用する理由について素晴らしい議論を行っています。そのフレームワークには少し歴史があり、ビジネスオブジェクトがドメインオブジェクトと並列するという彼のアイデアをさまざまな方法で見ることができます。しかし、優れたDDDの理想をより忠実に守ろうとすると、いつコラボレーションが過剰になるのだろうと思います。

集約ルートにIAuthorizationService、IValidator、IFactory、IRepositoryが含まれる場合、何が残っていますか?オブジェクトの状態をDraftからPublishedに変更するPublishメソッドがあると、クラスは非貧血ドメインオブジェクトと見なされますか?

あなたの考え?

92
SonOfPirate

ほとんどの混乱は、ドメインモデルにまったく存在してはならない機能に関係しているようです。

  • Persistenceをドメインモデルに含めることはできません。決して。これが、モデルの一部がモデルの別の部分を取得するなどの処理を行う必要がある場合にIRepositoryなどの抽象型に依存し、依存性注入または同様の手法を使用して実装を結び付ける理由です。だからレコードからそれを打つ。

  • Authorizationは、実際にはドメインの一部でない限り、ドメインモデルの一部ではありません。セキュリティソフトウェアを作成している場合。アプリケーションで何を実行できるかは、通常、ビジネス/ドメイン層の「エッジ」で処理されます。つまり、UIと統合機能が実際に通信できるパブリックパーツ-MVCのコントローラー、サービスまたは、SOAのメッセージングシステム自体...

  • Factories(ここでは抽象的なファクトリを意味していると思います)は、ドメインモデルに持つbadとは正確には異なりますが、ほとんど常に不要です。通常、オブジェクト作成の内部メカニズムが変化する可能性がある場合にのみ、ファクトリーがあります。ただし、ドメインモデルの実装は1つしかありません。つまり、常に同じコンストラクターと他の初期化コードを呼び出す1種類のファクトリーしか存在しません。

    必要に応じて「便利な」ファクトリー(コンストラクターパラメーターの一般的な組み合わせをカプセル化するクラスなど)を使用できますが、正直なところ、一般的に言えば、ドメインモデルに多数のファクトリーを配置している場合は、ラインを無駄にするだけです。コードの。

したがって、これらすべてを整頓すると、検証が終了します。ちょっとトリッキーなのはそれだけです。

検証isドメインモデルの一部ですが、アプリケーションの他のすべてのコンポーネントの一部でもあります。 UIとデータベースには、類似しているが異なる概念モデルに基づいて、独自の類似しているが異なる検証ルールがあります。オブジェクトにValidateメソッドが必要かどうかは実際には指定されていませんが、必要な場合でも、通常はバリデータークラスに委譲します(インターフェイスではなく、検証はnot抽象です)ドメインモデルでは、それは基本的です)。

バリデーターは技術的にはモデルの一部であることに注意してください。データや状態が含まれていないため、集約ルートにアタッチする必要はありません。ドメインモデルは概念的なものであり、通常は物理的にアセンブリまたはアセンブリのコレクションに変換されます。委任コードがオブジェクトモデルに非常に近い場所にある場合は、「貧血」の問題を強調しないでください。それはまだ重要です。

これが実際に下がるのは、DDDを実行する場合、ドメインが何であるかを理解する必要があるということです。持続性や承認などについてまだ話している場合は、間違った方向に進んでいます。ドメインは、システムの実行状態(物理的および概念的なオブジェクトと属性)を表します。オブジェクトおよび関係自体に直接関連しないものは、ドメインモデルの期間には属しません。

経験則として、ドメインモデルに何かが属しているかどうかを検討するときは、次の質問を自問してください。

"この機能は純粋に技術的な理由で変更される可能性がありますか?"言い換えれば、実際のビジネスまたはドメインに観測可能な変更があったためではないのですか?

答えが「はい」の場合、それはドメインモデルに属していません。ドメインの一部ではありません。

いつか、永続化と承認のインフラストラクチャを変更する可能性が非常に高くなります。したがって、これらはドメインの一部ではなく、アプリケーションの一部です。これは、並べ替えや検索などのアルゴリズムにも当てはまります。あなたのドメインは検索の抽象的な概念にのみ関係していて、それがどのように機能するのかではないので、バイナリ検索コードの実装をドメインモデルに押し込むべきではありません。

重要でないものをすべて取り除いた後、ドメインモデルが本当にanemicであることがわかった場合、それは次のように機能します。 DDDが単にプロジェクトにとって間違ったパラダイムであるというかなり良い兆候です。

一部のドメインは本当に貧弱です。ソーシャルブックマークアプリには、言うべき「ドメイン」があまりありません。すべてのオブジェクトは、基本的に機能のない単なるデータです。一方、販売およびCRMシステムにはかなり重いドメインがあります。 Rateエンティティをロードすると、実際にそのレートで実行できることが合理的な期待です。注文数量に合わせて、ボリュームディスカウントやプロモーションコード、その他すべての楽しいものを把握します。

通常データを保持するだけのドメインオブジェクトdoは、貧弱なドメインモデルがあることを意味しますが、必ずしもそうではありませんは、悪いデザインを作成したことを意味します。それは、ドメイン自体が貧弱であり、別の方法論を使用する必要があることを意味しているだけかもしれません。

67
Aaronaught

承認。ドメインオブジェクトがそのアクセス制御規則を維持する責任を負うべきか

いいえ。承認はそれ自体が懸念事項です。権限がないために無効になるコマンドは、できるだけ早くドメインの前に拒否する必要があります。つまり、ビルドするためにpotentialコマンドの承認を確認することさえあります。 UI(ユーザーに編集オプションを表示しないようにするため)。

承認がドメインモデルとは別にコンポーネント化されている場合、承認戦略をレイヤー間(UI内、さらにはサービスまたはコマンドハンドラー内)で共有する方が簡単です。

遭遇する可能性のあるトリッキーな部分の1つは、コマンドがユーザーの役割だけでなくビジネスデータ/ルールにも基づいて許可される場合と許可されない場合があるコンテキスト承認です。

検証。上記と同じ議論が検証に関係しています。

私はまた、(ほとんど)ドメインではなく、ノーと言います。検証はさまざまなコンテキストで行われ、検証ルールは多くの場合、コンテキスト間で異なります。集約によってカプセル化されたデータを検討する場合、有効または無効の単純で絶対的な意味はほとんどありません。

また、承認と同様に、UI、サービス、またはコマンドハンドラーなど、レイヤー全体で検証ロジックを利用します。ここでも、個別のコンポーネントの場合、検証でDRYを使用する方が簡単です。 。実用的な点から、検証(特にフレームワークを使用する場合)では、カプセル化する必要のあるデータを公開する必要があり、多くの場合、フィールドやプロパティにカスタム属性をアタッチする必要があります。これらは、ドメインモデル以外のクラスに配置することを強く望みます。

検証フレームワークの要件をエンティティに強制するのではなく、いくつかのプロパティをいくつかの類似したクラスに複製します。これは必然的にエンティティークラスを混乱させることになります。

オブジェクトの作成。ファクトリクラスとファクトリメソッド、またはインスタンスの「更新」。

私は1層の間接参照を使用しています。私の最近のプロジェクトでは、これは何かを作成するためのコマンド+ハンドラー、つまりCreateNewAccountCommandです。別の方法としては、常にファクトリを使用することもできます(ただし、エンティティオペレーションの残りの部分がファクトリクラスとは別のサービスクラスによって公開されている場合は扱いにくい場合があります)。

ただし、一般的には、オブジェクトを作成する際のデザインの選択肢をより柔軟にするよう努めています。 newは簡単で使い慣れていますが、必ずしも十分ではありません。ここで判断を使用し、システムのさまざまな部分が必要に応じてさまざまな戦略を使用できるようにすることが重要だと思います。

持続性。 ...ドメインオブジェクトにSaveメソッドがないのはなぜですか

これはめったに良い考えではありません。それをサポートする多くの共有経験があると思います。

集約ルートにIAuthorizationService、IValidator、IFactory、IRepositoryが含まれる場合、何が残っていますか?オブジェクトの状態をド​​ラフトから公開に変更するPublishメソッドは、クラスを非貧血ドメインオブジェクトと見なすのに十分ですか?

おそらく、ドメインモデルは、アプリケーションのこの部分では適切な選択ではありません。

6
quentin-starin

わかりました、ここで私のために行きます。次のように言って、これを回避します。

  • 時期尚早の最適化(および設計を含む)は、しばしば問題を引き起こす可能性があります。

  • IANMF(私はマーティンファウラーではありません);)

  • 汚い小さな秘密は、小規模なプロジェクト(おそらく中規模のプロジェクトであっても)では、アプローチの一貫性が重要になるということです。

認証

私にとって、認証と承認は常に横断的な関心事です。私の幸せな小さなJava=世界では、それはSpringセキュリティまたはApache Shiroフレームワークに委任されます。

検証私にとって、検証はオブジェクトの一部であり、オブジェクトが何であるかを定義していると考えています。

例えばCarオブジェクトには4ホイールがあります(OKいくつかの奇妙な例外がありますが、今のところ奇妙な3輪のCarは無視します)。 (私の世界では)4がない限り、Carは単に有効ではありません。そのため、検証はCarの定義の一部です。これは、ヘルパー検証クラスを作成できないという意味ではありません。

私の幸せなJava=世界では、Bean検証フレームワークを使用し、ほとんどのBeanフィールドに単純な注釈を使用しています。どのレイヤーにいても、オブジェクトを検証するのは簡単です。

オブジェクトの作成

Factoryクラスは注意して表示しています。 xyxFactoryFactoryクラスを頻繁に参照しています;)

依存性注入が保証されるケースに遭遇するまで(そして私がTDDアプローチに従おうとするため、これは頻繁に発生します)、私は必要に応じてnewオブジェクトを作成する傾向があります。

私の幸せなJavaの世界ではますますギスになりますが、ここでも春の王です。

持続性

だから、これはサークルとラウンドアバウトで行われる議論であり、私はいつもそれについて2つの考えを持っています。

オブジェクトを「純粋」な方法で見る場合、永続性はコアプロパティではなく、単に外部の問題であると言う人もいます。

他の人は、ドメインオブジェクトが暗黙的に「永続可能な」インターフェイスを実装しているという見方をします(ええ、私はここでストレッチしていることを知っています)。したがって、それらにさまざまなsavedelete etcメソッドを配置しても問題ありません。これは実用的なアプローチと見なされ、多くのORMテクノロジ(私の幸せなJPA Java世界では))はこの方法でオブジェクトを処理します。

横断的なセキュリティの懸念から、オブジェクトのsave/update/deleteメソッドを呼び出すサービスで編集/削除/追加/その他のアクセス許可が正しく設定されていることを確認します。私が本当に偏執的であるなら、ドメインオブジェクト自体に権限を設定することさえできます。

HTH!

4
Martijn Verburg

ジミー・ニルソンは、DDDに関する本でこのトピックに触れています。彼は貧血モデルから始め、後のプロジェクトで非貧血モデルに行き、最終的に貧血モデルに落ち着きました。彼の推論は、貧血モデルが異なるビジネスロジックを持つ複数のサービスで再利用される可能性があるというものでした。

トレードオフは、発見能力の欠如です。貧血モデルの操作に使用できる方法は、他の場所にある一連のサービス全体に広がっています。

2
Todd Smith

この質問はずっと前に尋ねられましたが、ドメイン駆動設計でタグ付けされています。質問自体には実践全体の根本的な誤解が含まれており、承認された回答を含む回答は根本的な誤解を永続させると思います。

DDDアーキテクチャには「ドメインモデル」はありません。

承認を例に取ってみましょう。質問について考えてみましょう。2人の異なるユーザーがシステムで認証することを想像してください。 1人のユーザーには特定のエンティティを変更する権限がありますが、他のユーザーにはありません。何故なの?

単純で人為的な例は、彼らが啓蒙する以上に混乱することが多いため、私は嫌いです。しかし、2つの異なるドメインがあるとします。 1つは、マーケティング代理店向けのCMSプラットフォームです。この代理店には、コピーライターやグラフィックアーティストが管理する必要のあるコンテンツがオンラインである多くの顧客がいます。コンテンツには、ブログの投稿だけでなく、さまざまな顧客のランディングページも含まれます。

もう1つのドメインは、靴会社の在庫管理です。このシステムは、フランスのメーカーからアメリカ大陸の配送センター、地元の市場の小売店、そして最終的に小売店で靴を購入する顧客まで、在庫を管理します。

承認ルールが両方の会社で同じであると思われる場合は、ドメイン外のサービスの候補として最適です。しかし、認可の規則が同じであるかどうかは疑問です。ユーザーの背後にある概念でさえ異なるでしょう。確かに言語は違うでしょう。マーケティング代理店はおそらく投稿者や資産所有者のような役割を持っているのに対し、靴会社はおそらく配送担当者、倉庫マネージャー、または店長のような役割を持っています。

これらの概念には、ドメインでモデル化する必要のあるあらゆる種類の許可ルールが関連付けられている可能性があります。ただし、同じアプリ内であっても、すべてが同じモデルの一部であるとは限りません。なぜなら、異なる境界コンテキストがあることを覚えておいてください。

したがって、おそらく承認のコンテキストでは、非貧血ドメインモデルは、靴の出荷を在庫の少ない店舗にルーティングしたり、クリックした広告に応じて適切なランディングページにサイト訪問者をルーティングしたりするコンテキストとは異なると考えるかもしれません。

貧血ドメインモデルを使用している場合は、コードの記述を開始する前に、コンテキストマッピングにより多くの時間を費やす必要があるだけかもしれません。

2
RibaldEddie